rad_face 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'rake_ext'
2
+
3
+ project(
4
+ name: "face",
5
+ official_name: "rad_face",
6
+ gem: true,
7
+ summary: "User Interface tools for Rad Framework",
8
+
9
+ author: "Alexey Petrushin",
10
+ homepage: "http://github.com/alexeypetrushin/rad_face"
11
+ )
@@ -0,0 +1,5 @@
1
+ rad.register :face, depends_on: [:controller, :template, :js, :web, :assets] do
2
+ require 'face/gems'
3
+ require 'face/require'
4
+ Rad::Face.new
5
+ end
@@ -0,0 +1,2 @@
1
+ themes_path: '/themes'
2
+ available_themes: []
@@ -0,0 +1,3 @@
1
+ rad.register :theme, depends_on: :face, scope: :cycle do
2
+ Rad::Face::Theme.new
3
+ end
data/lib/face/face.rb ADDED
@@ -0,0 +1,9 @@
1
+ class Rad::Face
2
+ attr_accessor :themes_path, :theme, :available_themes
3
+ require_attr :themes_path
4
+ def available_themes; @available_themes ||= [] end
5
+
6
+ def availiable_layouts
7
+ @availiable_layouts ||= {}
8
+ end
9
+ end
data/lib/face/gems.rb ADDED
@@ -0,0 +1,6 @@
1
+ if respond_to? :fake_gem
2
+ fake_gem 'rad_core'
3
+ fake_gem 'rad_ext'
4
+ fake_gem 'rad_js'
5
+ fake_gem 'rad_assets'
6
+ end
@@ -0,0 +1,49 @@
1
+ class Rad::Face::HamlBuilder < BasicObject
2
+ def initialize template, hash = {}
3
+ @template = template
4
+ @hash = ::Rad::Face::HtmlOpenObject.initialize_from(hash)
5
+ @array = []
6
+ end
7
+
8
+ def method_missing m, value = nil, &block
9
+ @hash[m] = ::Rad::Face::HamlBuilder.get_input @template, value, &block
10
+ nil
11
+ end
12
+
13
+ def add value = nil, &block
14
+ @array << ::Rad::Face::HamlBuilder.get_input(@template, value, &block)
15
+ nil
16
+ end
17
+
18
+ def get_value
19
+ !@array.empty? ? @array : @hash
20
+ end
21
+
22
+ def self.get_input template, value, &block
23
+ value = ::Rad::Face::HtmlOpenObject.initialize_from(value) if value.is_a? ::Hash
24
+
25
+ block_value = if block
26
+ if block.arity <= 0
27
+ template.must_be.defined
28
+ template.capture &block
29
+ else
30
+ b = ::Rad::Face::HamlBuilder.new template
31
+ block.call b
32
+ b.get_value
33
+ end
34
+ else
35
+ nil
36
+ end
37
+
38
+ if value and block_value
39
+ if block_value.is_a? ::Hash
40
+ value.merge! block_value
41
+ else
42
+ raise "Invalid usage!" if value.include? :content
43
+ value.content = block_value
44
+ end
45
+ end
46
+
47
+ value || block_value
48
+ end
49
+ end
@@ -0,0 +1,54 @@
1
+ class Rad::Face::HtmlOpenObject < OpenObject
2
+ HTML_ATTRIBUTES = [:id, :class]
3
+
4
+ def merge_html_attributes hash = {}
5
+ # html attributes
6
+ result = {}
7
+ HTML_ATTRIBUTES.each{|k| result[k.to_s] = self[k] if include? k}
8
+ html_attributes.each{|k, v| result[k.to_s] = v} if html_attributes?
9
+
10
+ # merging html attributes with hash
11
+ hash.each do |k, v|
12
+ k = k.to_s
13
+ if result.include?(k) and v.is_a?(String)
14
+ string = result[k].must_be.a Symbol, String
15
+ result[k] = "#{result[k]}#{v}"
16
+ else
17
+ result[k] = v
18
+ end
19
+ end
20
+ result
21
+ end
22
+
23
+ def self.initialize_from hash, deep = false
24
+ unless deep
25
+ ::Rad::Face::HtmlOpenObject.new.update hash
26
+ else
27
+ r = ::Rad::Face::HtmlOpenObject.new
28
+ hash.each do |k, v|
29
+ r[k] = if v.is_a? Hash
30
+ v.to_openobject
31
+ else
32
+ v
33
+ end
34
+ end
35
+ r
36
+ end
37
+ end
38
+
39
+ protected
40
+ # should threat newlines as blank
41
+ def method_missing m, arg = nil, &block
42
+ type = m[-1,1]
43
+ if type == '='
44
+ self[m[0..-2]] = arg
45
+ elsif type == '!'
46
+ self[m[0..-2]]
47
+ elsif type == '?'
48
+ value = self[m[0..-2]]
49
+ !(value.is_a?(String) ? (value =~ /\A\s*\z/) : value.blank?)
50
+ else
51
+ self[m]
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,18 @@
1
+ require 'rad_ext'
2
+
3
+ class Rad::Face
4
+ end
5
+
6
+ %w(
7
+ support
8
+ html_open_object
9
+ theme
10
+ haml_builder
11
+ themed_form_helper
12
+ view_builder
13
+ view_helper
14
+ face
15
+ ).each{|f| require "face/#{f}"}
16
+
17
+ Rad::Template::Context.inherit Rad::Face::ViewHelper
18
+ Rad::Controller::Abstract.inject theme: :theme
@@ -0,0 +1,42 @@
1
+ #
2
+ # content_or_self
3
+ #
4
+ [Hash, OpenObject].each do |aclass|
5
+ aclass.class_eval do
6
+ def hash?; true end
7
+ end
8
+ end
9
+
10
+ NilClass.class_eval do
11
+ def content; "" end
12
+ def hash?; false end
13
+ end
14
+
15
+ String.class_eval do
16
+ def content; self end
17
+ def hash?; false end
18
+ end
19
+
20
+ # OpenObject
21
+ OpenObject.class_eval do
22
+ def merge_html_attributes *a
23
+ raise "invalid usage, something wrong goin on, this method should be called only from instance of HtmlOpenObject class!"
24
+ end
25
+ alias_method :merge_html_attributes?, :merge_html_attributes
26
+ alias_method :merge_html_attributes!, :merge_html_attributes
27
+ end
28
+
29
+
30
+ #
31
+ # Automaticaly add content of action view to the :content content variable,
32
+ # to eliminate need for "content_for :content do ..." stuff.
33
+ #
34
+ Rad::Controller::Abstract::Render.class_eval do
35
+ protected
36
+ def render_content_with_content *a
37
+ content, context = render_content_without_content *a
38
+ context.content_for :content, content
39
+ return content, context
40
+ end
41
+ alias_method_chain :render_content, :content
42
+ end
data/lib/face/theme.rb ADDED
@@ -0,0 +1,48 @@
1
+ class Rad::Face::Theme
2
+ def name; @name || 'default' end
3
+ def name= name
4
+ # lots of other properties depends on :name, so we need to clean all of them if we change name
5
+ clear
6
+ @name = name.to_sym if name.present?
7
+ end
8
+
9
+ attr_writer :layout
10
+ def layout; @layout || 'default' end
11
+
12
+ def layout_template
13
+ @layout_template || layout_definition['layout_template'] || 'default'
14
+ end
15
+
16
+ def layout_template= layout_template
17
+ path = "#{rad.face.themes_path}/#{name}/layout_templates/#{layout_template}"
18
+ @layout_template = (layout_template and rad.template.exist?(path)) ? layout_template : 'default'
19
+ end
20
+
21
+ def layout_definition
22
+ self.class.layout_definition(name, layout) || self.class.layout_definition(name, 'default') || {}
23
+ end
24
+
25
+ protected
26
+ def clear
27
+ @layout_config, @layout_template, @layout = nil
28
+ end
29
+
30
+ class << self
31
+ def layout_definition theme, layout
32
+ path = "#{rad.face.themes_path}/#{theme}/layout_definitions/#{layout}.yml"
33
+ files = Rad::Environment::FilesHelper.find_files(path, rad.template.paths)
34
+ raise "multiple layout configs '#{layout}' for '#{theme}' theme!" if files.size > 1
35
+ if absolute_path = files.first
36
+ YAML.load_file(absolute_path).tap do |definition|
37
+ definition.must_be.a Hash
38
+ definition.must.include 'layout_template'
39
+ definition.must.include 'slots'
40
+ definition['slots'].must_be.a Hash
41
+ end
42
+ else
43
+ nil
44
+ end
45
+ end
46
+ cache_method_with_params_in_production :layout_definition
47
+ end
48
+ end
@@ -0,0 +1,74 @@
1
+ class Rad::Face::ThemedFormHelper
2
+ attr_accessor :template
3
+
4
+ def initialize template
5
+ self.template = template
6
+ end
7
+
8
+ def error_messages *errors
9
+ errors = errors.first if errors.size == 1 and errors.first.is_a? Array
10
+ template.render template.themed_partial('/forms/errors'), object: errors
11
+ end
12
+
13
+ def form_field options, &block
14
+ html_options = options.to_openobject
15
+ options = Rad::Face::HtmlOpenObject.new
16
+
17
+ # prepare options
18
+ %w(errors label description required theme).each do |k|
19
+ v = html_options.delete k
20
+ options[k] = v unless v.nil?
21
+ end
22
+ options.errors = options.errors.to_a
23
+
24
+ # CSS style
25
+ html_options.class ||= ""
26
+ html_options << " themed_input"
27
+
28
+ options.content = template.capture{block.call(html_options)}
29
+
30
+ html = template.render(template.themed_partial('/forms/field'), object: options)
31
+ template.concat html
32
+ end
33
+
34
+ def line *items
35
+ object = Rad::Face::HtmlOpenObject.initialize_from(items: items)
36
+ template.render template.themed_partial('/forms/line'), object: object
37
+ end
38
+
39
+ def self.generate_form_helper_methods methods
40
+ methods.each do |m|
41
+ define_method m do |*args|
42
+ options = args.extract_options!
43
+ template.capture do
44
+ form_field options do |html_options|
45
+ args << html_options
46
+ template.concat(template.send(m, *args))
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ #
55
+ # Form fields
56
+ #
57
+ methods = %w(
58
+ check_box_tag
59
+ field_set_tag
60
+ file_field_tag
61
+ password_field_tag
62
+ radio_button_tag
63
+ select_tag
64
+ text_field_tag
65
+ text_area_tag
66
+ )
67
+ generate_form_helper_methods methods
68
+
69
+ %w(
70
+ hidden_field_tag
71
+ submit_tag
72
+ ).each{|m| delegate m, to: :template}
73
+
74
+ end
@@ -0,0 +1,96 @@
1
+ class Rad::Face::ViewBuilder
2
+
3
+ # def self.generate_view_helper_methods methods
4
+ # methods.each do |folder, templates|
5
+ # templates.each do |template|
6
+ # code = %{\
7
+ # def #{template} *args, &block
8
+ # render_block "#{folder}", "#{template}", *args, &block
9
+ # end}
10
+ #
11
+ # eval code, binding, __FILE__, __LINE__
12
+ # end
13
+ # end
14
+ # end
15
+
16
+ attr_reader :template
17
+ def initialize template
18
+ @template = template
19
+ end
20
+
21
+
22
+ #
23
+ # Template methods
24
+ #
25
+ %w(
26
+ capture
27
+ concat
28
+ content_for
29
+ tag
30
+ render
31
+ themed_partial
32
+ controller
33
+ ).each do |m|
34
+ delegate m, to: :template
35
+ end
36
+
37
+
38
+ #
39
+ # Builders
40
+ #
41
+ def options *args, &block
42
+ opt = args.extract_options!
43
+ args.size.must_be.in 0..1
44
+ opt[:content] = args.first if args.size == 1
45
+
46
+ Rad::Face::HamlBuilder.get_input self.template, opt, &block
47
+ end
48
+
49
+
50
+ #
51
+ # Forms
52
+ #
53
+ def form_tag *args, &block
54
+ f = Rad::Face::ThemedFormHelper.new(template)
55
+
56
+ content = block ? capture{block.call(f)} : ""
57
+ object = Rad::Face::HtmlOpenObject.initialize_from(form_attributes: args, content: content)
58
+ html = render(
59
+ themed_partial('/forms/form'),
60
+ object: object
61
+ )
62
+
63
+ if block
64
+ template.concat html
65
+ else
66
+ html
67
+ end
68
+ end
69
+
70
+ def form_for *args, &block
71
+ model_helper, options = template.build_form_model_helper_and_form_options *args
72
+
73
+ form_tag options do |themed_form_helper|
74
+ model_helper.form_helper = themed_form_helper
75
+
76
+ block.call model_helper if block
77
+ end
78
+ end
79
+
80
+ def render_block template, *args, &block
81
+ opt = options *args, &block
82
+ html = render themed_partial(template), object: opt
83
+ block ? self.concat(html) : html
84
+ end
85
+
86
+ def prepare_form! options, *args
87
+ buff = template.form_tag *args
88
+ options[:begin] = buff
89
+ options[:end] = '</form>'
90
+ end
91
+
92
+ protected
93
+ def method_missing m, *args, &block
94
+ render_block "/#{m}", *args, &block
95
+ end
96
+ end
@@ -0,0 +1,36 @@
1
+ module Rad::Face::ViewHelper
2
+ inject theme: :theme
3
+
4
+ def b
5
+ @b ||= Rad::Face::ViewBuilder.new self
6
+ end
7
+ alias_method :builder, :b
8
+
9
+ def unique_id
10
+ @unique_id ||= 0
11
+ @unique_id += 1
12
+ end
13
+
14
+ def themed_partial partial
15
+ partial.must =~ /^\//
16
+ themed_partial = "/#{rad.face.themes_path}/#{theme.name}#{partial}"
17
+ if rad.template.exist? themed_partial
18
+ themed_partial
19
+ else
20
+ "/#{rad.face.themes_path}/default#{partial}"
21
+ end
22
+ end
23
+
24
+ def build_layout layout = nil
25
+ # Configuring
26
+ theme.layout = layout
27
+
28
+ # Rendering
29
+ theme.layout_definition['slots'].each do |slot_name, slots|
30
+ slots = Array(slots)
31
+ slots.each do |partial|
32
+ content_for slot_name, render(partial)
33
+ end
34
+ end
35
+ end
36
+ end
data/readme.md ADDED
@@ -0,0 +1,3 @@
1
+ # Face (UI toolkit) for the Rad Framework
2
+
3
+ Tool for rapid interface creation, [add more]
@@ -0,0 +1,120 @@
1
+ require "spec_helper"
2
+
3
+ #
4
+ # Don't use should ==, it doesn't works with OpenObject
5
+ #
6
+ describe "HamlBuilder use cases" do
7
+ class TemplateStub
8
+ def self.capture &block
9
+ block.call
10
+ self.output
11
+ end
12
+
13
+ class << self
14
+ attr_accessor :output
15
+ end
16
+ end
17
+
18
+ def build *args, &block
19
+ opt = args.extract_options!
20
+ args.size.must_be.in 0..1
21
+ opt[:content] = args.first if args.size == 1
22
+
23
+ Rad::Face::HamlBuilder.get_input(TemplateStub, opt, &block)
24
+ end
25
+
26
+ it "should accept OpenObject as input" do
27
+ build({a: :b}.to_openobject).should == {a: :b}
28
+ end
29
+
30
+ it "hash" do
31
+ (build do |o|
32
+ o.a :b
33
+ end).should == {a: :b}
34
+
35
+ build(a: :b).should == {a: :b}
36
+ end
37
+
38
+ it "array" do
39
+ (build do |a|
40
+ a.add 1
41
+ a.add 2
42
+ end).should == {content: [1, 2]}
43
+
44
+ (build do |o|
45
+ o.a :b
46
+ o.ar do |a|
47
+ a.add 1
48
+ a.add 2
49
+ end
50
+ end).should == {a: :b, ar: [1, 2]}
51
+ end
52
+
53
+ it "capture" do
54
+ build("value").should == {content: "value"}
55
+
56
+ (build do
57
+ TemplateStub.output = "value"
58
+ end).should == {content: "value"}
59
+
60
+ (build do |o|
61
+ o.content do
62
+ TemplateStub.output = "value"
63
+ end
64
+ end).should == {content: "value"}
65
+
66
+ (build do |o|
67
+ o.value do
68
+ TemplateStub.output = "value"
69
+ end
70
+ end).should == {value: "value"}
71
+ end
72
+
73
+ it "invalid usage" do
74
+ -> {
75
+ build "value" do
76
+ TemplateStub.output = "value"
77
+ end
78
+ }.should raise_error(/Invalid usage!/)
79
+ end
80
+
81
+ it "merge" do
82
+ (build a: :b do
83
+ TemplateStub.output = "value"
84
+ end).should == {a: :b, content: "value"}
85
+
86
+ (build a: :b do |o|
87
+ o.c :d
88
+ end).should == {a: :b, c: :d}
89
+ end
90
+
91
+ it "nested" do
92
+ (build a: :b do |o|
93
+ o.a do |o|
94
+ o.b :c
95
+ end
96
+ end).should == {
97
+ a: {b: :c}
98
+ }
99
+ end
100
+
101
+ it "complex" do
102
+ (build a: :b do |o|
103
+ o.hs do |h|
104
+ h.c :d
105
+ end
106
+ o.ar do |a|
107
+ a.add 1
108
+ a.add 2
109
+ end
110
+ o.html do
111
+ TemplateStub.output = "value"
112
+ end
113
+ end).should == {
114
+ a: :b,
115
+ hs: {c: :d},
116
+ ar: [1, 2],
117
+ html: 'value'
118
+ }
119
+ end
120
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ describe "HamlBuilder use cases" do
4
+ def build_hoo h
5
+ Rad::Face::HtmlOpenObject.initialize_from h
6
+ end
7
+
8
+ it "should threat newlines as blank" do
9
+ o = build_hoo(blank_line: "\n\n \t", line_with_content: "abc")
10
+ o.blank_line?.should be_false
11
+ o.line_with_content?.should be_true
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ require 'rspec_ext'
2
+
3
+ require 'rad_ext'
4
+ require 'rad/spec'
5
+
6
+ rad.face
@@ -0,0 +1,5 @@
1
+ <% if object.content? %>
2
+ content present
3
+ <% else %>
4
+ content blank
5
+ <% end %>
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Template" do
4
+ with_view_path spec_dir
5
+
6
+ inject template: :template
7
+
8
+ before do
9
+ @view_builder = Rad::Face::ViewBuilder.new rad.template
10
+ @view_builder.stub!(:themed_partial){|partial| partial}
11
+ end
12
+
13
+ it "should threat string with newlines as a blank string" do
14
+ @view_builder.render_block('/blank_symbols', content: "\n").should =~ /content blank/
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rad_face
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Alexey Petrushin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-07-04 00:00:00.000000000 +04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rad_core
17
+ requirement: &2956520 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *2956520
26
+ - !ruby/object:Gem::Dependency
27
+ name: rad_ext
28
+ requirement: &2956210 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *2956210
37
+ - !ruby/object:Gem::Dependency
38
+ name: rad_js
39
+ requirement: &2955920 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *2955920
48
+ - !ruby/object:Gem::Dependency
49
+ name: rad_assets
50
+ requirement: &2955680 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *2955680
59
+ description:
60
+ email:
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - Rakefile
66
+ - readme.md
67
+ - lib/components/face.rb
68
+ - lib/components/face.yml
69
+ - lib/components/theme.rb
70
+ - lib/face/face.rb
71
+ - lib/face/gems.rb
72
+ - lib/face/haml_builder.rb
73
+ - lib/face/html_open_object.rb
74
+ - lib/face/require.rb
75
+ - lib/face/support.rb
76
+ - lib/face/theme.rb
77
+ - lib/face/themed_form_helper.rb
78
+ - lib/face/view_builder.rb
79
+ - lib/face/view_helper.rb
80
+ - spec/haml_builder_spec.rb
81
+ - spec/html_open_object_spec.rb
82
+ - spec/spec_helper.rb
83
+ - spec/view_builder_spec/blank_symbols.html.erb
84
+ - spec/view_builder_spec.rb
85
+ has_rdoc: true
86
+ homepage: http://github.com/alexeypetrushin/rad_face
87
+ licenses: []
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 1.5.1
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: User Interface tools for Rad Framework
110
+ test_files: []