abstract_interface 0.1.0

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.
data/Rakefile ADDED
@@ -0,0 +1,64 @@
1
+ require 'rake'
2
+ require 'fileutils'
3
+ current_dir = File.expand_path(File.dirname(__FILE__))
4
+ Dir.chdir current_dir
5
+
6
+
7
+ #
8
+ # Specs
9
+ #
10
+ require 'spec/rake/spectask'
11
+
12
+ task :default => :spec
13
+
14
+ Spec::Rake::SpecTask.new('spec') do |t|
15
+ t.spec_files = FileList["spec/**/*_spec.rb"].select{|f| f !~ /\/_/}
16
+ t.libs = ["#{current_dir}/lib"]
17
+ end
18
+
19
+
20
+ #
21
+ # Gem
22
+ #
23
+ require 'rake/clean'
24
+ require 'rake/gempackagetask'
25
+
26
+ gem_options = {
27
+ :name => "abstract_interface",
28
+ :version => "0.1.0",
29
+ :summary => "Ruby language extensions",
30
+ :dependencies => %w(facets)
31
+ }
32
+
33
+ spec = Gem::Specification.new do |s|
34
+ gem_options.delete(:dependencies).each{|d| s.add_dependency d}
35
+ gem_options.each{|k, v| s.send "#{k}=", v}
36
+
37
+ s.author = "Alexey Petrushin"
38
+ s.homepage = "http://github.com/alexeypetrushin/#{gem_options[:name]}"
39
+ s.require_path = "lib"
40
+ s.files = (%w{Rakefile readme.md} + Dir.glob("{lib,spec}/**/*"))
41
+
42
+ s.platform = Gem::Platform::RUBY
43
+ s.has_rdoc = true
44
+ end
45
+
46
+ package_dir = "#{current_dir}/build"
47
+ Rake::GemPackageTask.new(spec) do |p|
48
+ p.need_tar = true if RUBY_PLATFORM !~ /mswin/
49
+ p.need_zip = true
50
+ p.package_dir = package_dir
51
+ end
52
+
53
+ task :push do
54
+ # dir = Dir.chdir package_dir do
55
+ gem_file = Dir.glob("#{package_dir}/#{gem_options[:name]}*.gem").first
56
+ system "gem push #{gem_file}"
57
+ # end
58
+ end
59
+
60
+ task :clean do
61
+ system "rm -r #{package_dir}"
62
+ end
63
+
64
+ task :release => [:gem, :push, :clean]
@@ -0,0 +1,64 @@
1
+ module AbstractInterface
2
+ class << self
3
+ inject :logger => :logger, :config => :config, :environment => :environment
4
+
5
+ attr_accessor :plugin_name
6
+ attr_accessor :layout_configurations_dir
7
+
8
+ def generate_helper_methods *args
9
+ AbstractInterface::ViewBuilder.generate_helper_methods *args
10
+ end
11
+
12
+ def available_themes; @available_themes ||= [] end
13
+
14
+ def theme_metadata theme
15
+ logger.warn "Complex calculation (AbstractInterface.theme_metadata) called in production!" if config.production?
16
+
17
+ metadata = {}
18
+
19
+ name = "/#{Crystal::Template::DIRECTORY_NAME}/#{THEMES_DIR}/#{theme}/metadata.rb"
20
+ if environment.file_exist? name
21
+ fname = environment.find_file name
22
+ code = File.read fname
23
+ metadata = eval code
24
+ metadata.must_be.a Hash
25
+ end
26
+
27
+ metadata.to_openobject
28
+ end
29
+
30
+ def layouts_defined?
31
+ !!layout_configurations_dir
32
+ end
33
+
34
+ # Place definitions of your layouts into :layout_configurations_dir folder, you can have multiple such directories
35
+ def layout_definitions_without_cache theme
36
+ name = "#{layout_configurations_dir.must_be.present}/#{theme}.yml"
37
+ raise "File '#{name}' not exist!" unless environment.file_exist? name
38
+
39
+ result = {}
40
+ environment.find_files(name).each do |fname|
41
+ lds = YAML.load_file(fname)
42
+ validate_layout_definition!(lds, theme)
43
+ result.merge! lds
44
+ end
45
+ result
46
+ end
47
+ cache_method_with_params_in_production :layout_definitions
48
+
49
+ protected
50
+ def validate_layout_definition! lds, theme
51
+ lds.must_be.a Hash
52
+ unless lds.include?('default')
53
+ raise "No 'default' layout definition for '#{theme}' Theme (there always should be definition for 'default' layout)!"
54
+ end
55
+ lds.each do |theme_name, ld|
56
+ ld.must_be.a Hash
57
+ ld.must.include 'layout_template'
58
+ ld.must.include 'slots'
59
+ ld['slots'].must_be.a Hash
60
+ end
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,24 @@
1
+ module AbstractInterface
2
+ module ControllerHelper
3
+ def current_theme
4
+ @current_theme ||= AbstractInterface::Theme.new
5
+ end
6
+
7
+ # TODO1
8
+ # def build_layout layout = nil
9
+ # # Configuring
10
+ # current_theme.layout = layout
11
+ #
12
+ # # Rendering
13
+ # current_theme.layout_definition['slots'].each do |slot_name, slots|
14
+ # slots = Array(slots)
15
+ # slots.each do |partial|
16
+ # content_for slot_name do
17
+ # render :partial => partial
18
+ # end
19
+ # end
20
+ # end
21
+ # end
22
+
23
+ end
24
+ end
@@ -0,0 +1,56 @@
1
+ module AbstractInterface
2
+ class HamlBuilder < BasicObject
3
+ def initialize template, hash = OpenObject.new
4
+ @template = template
5
+ @hash, @array = hash, []
6
+ end
7
+
8
+ def method_missing m, value = nil, &block
9
+ @hash[m] = HamlBuilder.get_input @template, value, &block
10
+ nil
11
+ end
12
+
13
+ def add value = nil, &block
14
+ @array << HamlBuilder.get_input(@template, value, &block)
15
+ nil
16
+ end
17
+
18
+ # def add_item content, opt = {}, &block
19
+ # opt[:content] = content
20
+ # opt[:content] ||= @template.capture &block if block
21
+ # add opt
22
+ # end
23
+
24
+ def get_value
25
+ !@array.empty? ? @array : @hash
26
+ end
27
+
28
+ def self.get_input template, value, &block
29
+ value = value.to_openobject if value.is_a? Hash
30
+
31
+ block_value = if block
32
+ if block.arity <= 0
33
+ template.must_be.defined
34
+ template.capture &block
35
+ else
36
+ b = HamlBuilder.new template
37
+ block.call b
38
+ b.get_value
39
+ end
40
+ else
41
+ nil
42
+ end
43
+
44
+ if value and block_value
45
+ if block_value.is_a? Hash
46
+ value = value.merge block_value
47
+ else
48
+ raise "Invalid usage!" if value.include? :content
49
+ value.content = block_value
50
+ end
51
+ end
52
+
53
+ value || block_value
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,40 @@
1
+ # content_or_self
2
+ [Hash, OpenObject].each do |aclass|
3
+ aclass.class_eval do
4
+ def hash?; true end
5
+ end
6
+ end
7
+
8
+ NilClass.class_eval do
9
+ def content; "" end
10
+ def hash?; false end
11
+ end
12
+
13
+ String.class_eval do
14
+ def content; self end
15
+ def hash?; false end
16
+ end
17
+
18
+ # OpenObject
19
+ OpenObject.class_eval do
20
+ HTML_ATTRIBUTES = [:id, :class]
21
+
22
+ def merge_html_attributes hash
23
+ # html attributes
24
+ result = {}
25
+ HTML_ATTRIBUTES.each{|k| result[k.to_s] = self[k] if include? k}
26
+ html_attributes.each{|k, v| result[k.to_s] = v} if html_attributes?
27
+
28
+ # merging html attributes with hash
29
+ hash.each do |k, v|
30
+ k = k.to_s
31
+ if result.include?(k) and v.is_a?(String)
32
+ string = result[k].must_be.a [Symbol, String]
33
+ result[k] = "#{result[k]}#{v}"
34
+ else
35
+ result[k] = v
36
+ end
37
+ end
38
+ result
39
+ end
40
+ end
@@ -0,0 +1,37 @@
1
+ module AbstractInterface
2
+ class Theme
3
+ attr_writer :name, :layout_template, :layout
4
+ def name; @name || 'default' end
5
+ def layout; @layout || 'default' end
6
+
7
+ def layout_template
8
+ if @layout_template
9
+ # Check if this template exists
10
+ exists = layout_definitions.any?{|layout_name, ld| ld['layout_template'] == @layout_template}
11
+ exists ? @layout_template : 'default'
12
+ else
13
+ layout_definition['layout_template'] || 'default'
14
+ end
15
+ end
16
+
17
+ def layout_definition
18
+ layout_definitions[layout] || layout_definitions['default'] || {}
19
+ end
20
+
21
+ def layout_definitions
22
+ if AbstractInterface.layouts_defined?
23
+ AbstractInterface.layout_definitions(name)
24
+ else
25
+ {}
26
+ end
27
+ end
28
+
29
+ def available_layouts_names
30
+ layout_definitions.keys
31
+ end
32
+
33
+ def metadata
34
+ AbstractInterface.theme_metadata(name)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,74 @@
1
+ module AbstractInterface
2
+ class ThemedFormHelper
3
+ attr_accessor :template
4
+
5
+ def initialize template
6
+ self.template = template
7
+ end
8
+
9
+ def error_messages *errors
10
+ errors = errors.first if errors.size == 1 and errors.first.is_a? Array
11
+ template.render template.themed_partial('forms/errors'), :object => errors
12
+ end
13
+
14
+ def form_field options, &block
15
+ html_options = options.to_openobject
16
+ options = OpenObject.new
17
+
18
+ # prepare options
19
+ %w(errors label description required theme).each do |k|
20
+ v = html_options.delete k
21
+ options[k] = v unless v.nil?
22
+ end
23
+ options.errors = options.errors.to_a
24
+
25
+ # CSS style
26
+ html_options.class ||= ""
27
+ html_options << " themed_input"
28
+
29
+ options.content = template.capture{block.call(html_options)}
30
+
31
+ html = template.render(template.themed_partial('forms/field'), :object => options)
32
+ template.concat html
33
+ end
34
+
35
+ def line *items
36
+ template.render template.themed_partial('forms/line'), :object => {:items => items, :delimiter => false}.to_openobject
37
+ end
38
+
39
+ def line_with_delimiters *items
40
+ template.render template.themed_partial('forms/line'), :object => {:items => items, :delimiter => true}.to_openobject
41
+ end
42
+
43
+
44
+ #
45
+ # Form fields
46
+ #
47
+ %w(
48
+ check_box_tag
49
+ field_set_tag
50
+ file_field_tag
51
+ password_field_tag
52
+ radio_button_tag
53
+ select_tag
54
+ text_field_tag
55
+ text_area_tag
56
+ ).each do |m|
57
+ define_method m do |*args|
58
+ options = args.extract_options!
59
+ template.capture do
60
+ form_field options do |html_options|
61
+ args << html_options
62
+ template.concat(template.send(m, *args))
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ %w(
69
+ hidden_field_tag
70
+ submit_tag
71
+ ).each{|m| delegate m, :to => :template}
72
+
73
+ end
74
+ end
@@ -0,0 +1,100 @@
1
+ module AbstractInterface
2
+ class ViewBuilder
3
+
4
+ def self.generate_helper_methods methods
5
+ methods.each do |folder, templates|
6
+ templates.each do |template|
7
+ code = %{\
8
+ def #{template} *args, &block
9
+ render_haml_builder "#{folder}", "#{template}", *args, &block
10
+ end}
11
+
12
+ eval code, binding, __FILE__, __LINE__
13
+ end
14
+ end
15
+ end
16
+
17
+
18
+ attr_reader :template
19
+ def initialize template
20
+ @template = template
21
+ end
22
+
23
+
24
+ #
25
+ # Template methods
26
+ #
27
+ %w(
28
+ capture
29
+ concat
30
+ content_for
31
+ tag
32
+ render
33
+ themed_resource
34
+ themed_partial
35
+ controller
36
+ ).each do |m|
37
+ delegate m, :to => :template
38
+ end
39
+
40
+
41
+ #
42
+ # Builders
43
+ #
44
+ def options *args, &block
45
+ opt = args.extract_options!
46
+ args.size.must_be.in 0..1
47
+ opt[:content] = args.first if args.size == 1
48
+
49
+ AbstractInterface::HamlBuilder.get_input self.template, opt, &block
50
+ end
51
+
52
+
53
+ #
54
+ # Forms
55
+ #
56
+ def form_tag *args, &block
57
+ f = ThemedFormHelper.new(template)
58
+
59
+ content = block ? capture{block.call(f)} : ""
60
+ html = render(
61
+ themed_partial('forms/form'),
62
+ :object => {:form_attributes => options, :content => content}.to_openobject
63
+ )
64
+
65
+ if block
66
+ template.concat html
67
+ else
68
+ html
69
+ end
70
+ end
71
+
72
+ def form_for *args, &block
73
+ model_helper, options = template.build_form_model_helper_and_form_options *args
74
+
75
+ form_tag options do |themed_form_helper|
76
+ model_helper.form_helper = themed_form_helper
77
+
78
+ block.call model_helper if block
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def render_haml_builder folder, template, *args, &block
85
+ opt = options *args, &block
86
+
87
+ partial = "#{folder}/#{template}"
88
+
89
+ html = render themed_partial(partial), :object => opt
90
+
91
+ block ? self.concat(html) : html
92
+ end
93
+
94
+ def prepare_form! options, *args
95
+ buff = template.form_tag *args
96
+ options[:begin] = buff
97
+ options[:end] = '</form>'
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,42 @@
1
+ module AbstractInterface
2
+ module ViewHelper
3
+ def b
4
+ @b ||= AbstractInterface::ViewBuilder.new self
5
+ end
6
+ alias_method :builder, :b
7
+
8
+ def themed_resource resource
9
+ "/#{AbstractInterface.plugin_name.must_not_be.blank}/#{THEMES_DIR}/#{current_theme.name}/#{resource}"
10
+ end
11
+
12
+ def themed_partial partial
13
+ themed_partial = "/#{THEMES_DIR}/#{current_theme.name}/#{partial}"
14
+ if Crystal::Template.exist? themed_partial
15
+ themed_partial
16
+ else
17
+ "/#{THEMES_DIR}/default/#{partial}"
18
+ end
19
+ end
20
+
21
+ def current_theme
22
+ controller.current_theme
23
+ end
24
+
25
+ # TODO1
26
+ # def build_layout layout = nil
27
+ # # Configuring
28
+ # current_theme.layout = layout
29
+ #
30
+ # # Rendering
31
+ # current_theme.layout_definition['slots'].each do |slot_name, slots|
32
+ # slots = Array(slots)
33
+ # slots.each do |partial|
34
+ # content_for slot_name do
35
+ # render :partial => partial
36
+ # end
37
+ # end
38
+ # end
39
+ # end
40
+
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ require 'abstract_interface/support'
2
+
3
+ module AbstractInterface
4
+ THEMES_DIR = 'themes'
5
+
6
+ autoload :Theme, 'abstract_interface/theme'
7
+ autoload :HamlBuilder, 'abstract_interface/haml_builder'
8
+ autoload :ThemedFormHelper, 'abstract_interface/themed_form_helper'
9
+ autoload :ViewBuilder, 'abstract_interface/view_builder'
10
+ autoload :ViewHelper, 'abstract_interface/view_helper'
11
+ autoload :ControllerHelper, 'abstract_interface/controller_helper'
12
+ end
13
+
14
+ require 'abstract_interface/abstract_interface'
15
+
16
+ Crystal::ControllerContext.inherit AbstractInterface::ViewHelper
17
+ Crystal::AbstractController.inherit AbstractInterface::ControllerHelper
18
+
19
+ # TODO2
20
+ # ActionView::Base.field_error_proc = lambda do |html_tag, instance|
21
+ # html_tag
22
+ # end
23
+
24
+ # TODO1
25
+ # Rails.development{RailsExt.create_public_symlinks!} # rails_ext.css, rails_ext.js in development mode
data/readme.md ADDED
@@ -0,0 +1,3 @@
1
+ # Abstract Interface for the Crystal Framework
2
+
3
+ Tool for rapid interface creation, [add more]
@@ -0,0 +1,121 @@
1
+ dir = File.expand_path(File.dirname(__FILE__))
2
+ require "#{dir}/helper"
3
+
4
+ #
5
+ # Don't use should ==, it doesn't works with OpenObject
6
+ #
7
+ describe "HamlBuilder use cases" do
8
+ class TemplateStub
9
+ def self.capture &block
10
+ block.call
11
+ self.output
12
+ end
13
+
14
+ class << self
15
+ attr_accessor :output
16
+ end
17
+ end
18
+
19
+ def build *args, &block
20
+ opt = args.extract_options!
21
+ args.size.must_be.in 0..1
22
+ opt[:content] = args.first if args.size == 1
23
+
24
+ AbstractInterface::HamlBuilder.get_input(TemplateStub, opt, &block)
25
+ end
26
+
27
+ it "should accept OpenObject as input" do
28
+ build({:a => :b}.to_openobject).should == {:a => :b}
29
+ end
30
+
31
+ it "hash" do
32
+ (build do |o|
33
+ o.a :b
34
+ end).should == {:a => :b}
35
+
36
+ build(:a => :b).should == {:a => :b}
37
+ end
38
+
39
+ it "array" do
40
+ (build do |a|
41
+ a.add 1
42
+ a.add 2
43
+ end).should == {:content => [1, 2]}
44
+
45
+ (build do |o|
46
+ o.a :b
47
+ o.ar do |a|
48
+ a.add 1
49
+ a.add 2
50
+ end
51
+ end).should == {:a => :b, :ar => [1, 2]}
52
+ end
53
+
54
+ it "capture" do
55
+ build("value").should == {:content => "value"}
56
+
57
+ (build do
58
+ TemplateStub.output = "value"
59
+ end).should == {:content => "value"}
60
+
61
+ (build do |o|
62
+ o.content do
63
+ TemplateStub.output = "value"
64
+ end
65
+ end).should == {:content => "value"}
66
+
67
+ (build do |o|
68
+ o.value do
69
+ TemplateStub.output = "value"
70
+ end
71
+ end).should == {:value => "value"}
72
+ end
73
+
74
+ it "invalid usage" do
75
+ lambda{
76
+ build "value" do
77
+ TemplateStub.output = "value"
78
+ end
79
+ }.should raise_error(/Invalid usage!/)
80
+ end
81
+
82
+ it "merge" do
83
+ (build :a => :b do
84
+ TemplateStub.output = "value"
85
+ end).should == {:a => :b, :content => "value"}
86
+
87
+ (build :a => :b do |o|
88
+ o.c :d
89
+ end).should == {:a => :b, :c => :d}
90
+ end
91
+
92
+ it "nested" do
93
+ (build :a => :b do |o|
94
+ o.a do |o|
95
+ o.b :c
96
+ end
97
+ end).should == {
98
+ :a => {:b => :c}
99
+ }
100
+ end
101
+
102
+ it "complex" do
103
+ (build :a => :b do |o|
104
+ o.hs do |h|
105
+ h.c :d
106
+ end
107
+ o.ar do |a|
108
+ a.add 1
109
+ a.add 2
110
+ end
111
+ o.html do
112
+ TemplateStub.output = "value"
113
+ end
114
+ end).should == {
115
+ :a => :b,
116
+ :hs => {:c => :d},
117
+ :ar => [1, 2],
118
+ :html => 'value'
119
+ }
120
+ end
121
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,9 @@
1
+ dir = File.expand_path(File.dirname(__FILE__))
2
+ lib_dir = File.expand_path("#{dir}/../../../lib")
3
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include? lib_dir
4
+
5
+ require 'crystal/support'
6
+ require 'abstract_interface/support'
7
+ require "abstract_interface/haml_builder"
8
+
9
+ require 'spec_ext'
data/spec/spec.opts ADDED
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format progress
3
+ --loadby mtime
4
+ --reverse
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: abstract_interface
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Alexey Petrushin
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-11 00:00:00 +04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: facets
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description:
36
+ email:
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - Rakefile
45
+ - readme.md
46
+ - lib/abstract_interface/abstract_interface.rb
47
+ - lib/abstract_interface/controller_helper.rb
48
+ - lib/abstract_interface/haml_builder.rb
49
+ - lib/abstract_interface/support.rb
50
+ - lib/abstract_interface/theme.rb
51
+ - lib/abstract_interface/themed_form_helper.rb
52
+ - lib/abstract_interface/view_builder.rb
53
+ - lib/abstract_interface/view_helper.rb
54
+ - lib/abstract_interface.rb
55
+ - spec/haml_builder_spec.rb
56
+ - spec/helper.rb
57
+ - spec/spec.opts
58
+ has_rdoc: true
59
+ homepage: http://github.com/alexeypetrushin/abstract_interface
60
+ licenses: []
61
+
62
+ post_install_message:
63
+ rdoc_options: []
64
+
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ hash: 3
82
+ segments:
83
+ - 0
84
+ version: "0"
85
+ requirements: []
86
+
87
+ rubyforge_project:
88
+ rubygems_version: 1.3.7
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: Ruby language extensions
92
+ test_files: []
93
+