rpv_core 0.2
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 +3 -0
- data/bin/rpv +68 -0
- data/lib/rpv/autoload.rb +36 -0
- data/lib/rpv/base_presenter.rb +41 -0
- data/lib/rpv/event_publisher.rb +125 -0
- data/lib/rpv/generators/app_generator.rb +17 -0
- data/lib/rpv/generators/base.rb +130 -0
- data/lib/rpv/generators/presenter_generator.rb +28 -0
- data/lib/rpv/generators/templates/app/README +12 -0
- data/lib/rpv/generators/templates/app/Rakefile +3 -0
- data/lib/rpv/generators/templates/app/app/main.rb +4 -0
- data/lib/rpv/generators/templates/app/app/setup.rb +11 -0
- data/lib/rpv/generators/templates/app/spec/spec_helper.rb +5 -0
- data/lib/rpv/generators/templates/app/tasks/rspec.rake +9 -0
- data/lib/rpv/generators/templates/presenter/presenter.rb.erb +10 -0
- data/lib/rpv/generators/templates/presenter/presenter_spec.rb.erb +22 -0
- data/lib/rpv/inflector.rb +68 -0
- data/lib/rpv/method.rb +13 -0
- data/lib/rpv/method_helper.rb +9 -0
- data/lib/rpv/option_extractor.rb +15 -0
- data/lib/rpv/spec_helper.rb +118 -0
- data/lib/rpv/wiring/base.rb +49 -0
- data/lib/rpv/wiring/callable.rb +20 -0
- data/lib/rpv/wiring/converting.rb +36 -0
- data/lib/rpv/wiring/helpers.rb +23 -0
- data/lib/rpv/wiring/simple.rb +19 -0
- data/lib/rpv/wiring/unidirectional.rb +38 -0
- data/lib/rpv/wiring.rb +4 -0
- data/lib/rpv.rb +20 -0
- data/spec/autoload_spec.rb +24 -0
- data/spec/base_presenter_spec.rb +16 -0
- data/spec/event_publisher_spec.rb +121 -0
- data/spec/files/autoload/b/some_module.rb +2 -0
- data/spec/files/autoload/some_class.rb +3 -0
- data/spec/files/bar/presenter.rb.erb +5 -0
- data/spec/files/bar/view.rb.erb +3 -0
- data/spec/files/foo/Rakefile +0 -0
- data/spec/files/foo/app/main.rb.erb +1 -0
- data/spec/files/foo/app/models/templates/lala.html.erb +2 -0
- data/spec/generators/base_spec.rb +81 -0
- data/spec/generators/presenter_spec.rb +27 -0
- data/spec/method_helper_spec.rb +19 -0
- data/spec/method_spec.rb +12 -0
- data/spec/option_extractor_spec.rb +22 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/spec_helper_spec.rb +15 -0
- data/spec/wiring/base_spec.rb +17 -0
- data/spec/wiring/callable_spec.rb +50 -0
- data/spec/wiring/helpers_spec.rb +29 -0
- data/spec/wiring/simple_spec.rb +47 -0
- data/spec/wiring/unidirectional_spec.rb +46 -0
- data/tasks/rdoc.rake +10 -0
- data/tasks/rspec.rake +10 -0
- metadata +118 -0
data/Rakefile
ADDED
data/bin/rpv
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#TODO ^
|
3
|
+
|
4
|
+
require 'fileutils'
|
5
|
+
require 'rpv/inflector'
|
6
|
+
|
7
|
+
def usage
|
8
|
+
puts <<-END
|
9
|
+
rpv new APP_NAME
|
10
|
+
#creates a new app in ./APP_NAME
|
11
|
+
rpv g GENERATOR ARG:VALUE
|
12
|
+
#invokes a generator
|
13
|
+
#call without argument to see what generators are available
|
14
|
+
#This only works when run in an application's directory
|
15
|
+
END
|
16
|
+
exit(0)
|
17
|
+
end
|
18
|
+
|
19
|
+
def generate
|
20
|
+
$LOAD_PATH << FileUtils.pwd
|
21
|
+
begin
|
22
|
+
require 'app/setup'
|
23
|
+
RPV::Generators.require_all
|
24
|
+
rescue LoadError
|
25
|
+
puts "Could not load ./app/setup.rb; make sure pwd is the application's dir"
|
26
|
+
exit(0)
|
27
|
+
end
|
28
|
+
if ARGV[1].nil?
|
29
|
+
RPV::Generators.all.each do |c|
|
30
|
+
puts " " + c.to_s.gsub("RPV::Generators::","").underscore.gsub("_generator", "") + ' #' + c.describe
|
31
|
+
puts " args: " + c.argument_definitions.join(", ")
|
32
|
+
end
|
33
|
+
else
|
34
|
+
args = ARGV.dup
|
35
|
+
args.delete_at(0)
|
36
|
+
generator_class = ("RPV::Generators::" + (args.delete_at(0) + "_generator").camelize).constantize
|
37
|
+
generator_args = {}
|
38
|
+
args.each do |a|
|
39
|
+
if a =~ /(.*):(.*)/
|
40
|
+
generator_args[$1.to_sym] = $2
|
41
|
+
end
|
42
|
+
end
|
43
|
+
generator_class.new.execute *[generator_args.merge({:to_path => '.'})]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
case ARGV[0]
|
50
|
+
when 'new'
|
51
|
+
usage if ARGV[1].nil?
|
52
|
+
require 'rpv/generators/app_generator'
|
53
|
+
RPV::Generators::AppGenerator.new.execute :to_path => "./#{ARGV[1]}"
|
54
|
+
when 'g' #generate
|
55
|
+
generate
|
56
|
+
when 'r' #run
|
57
|
+
$LOAD_PATH << FileUtils.pwd
|
58
|
+
begin
|
59
|
+
require 'app/main'
|
60
|
+
rescue LoadError
|
61
|
+
puts "Could not load ./app/main.rb; make sure pwd is the application's dir"
|
62
|
+
exit(0)
|
63
|
+
end
|
64
|
+
else nil
|
65
|
+
usage
|
66
|
+
end
|
67
|
+
|
68
|
+
|
data/lib/rpv/autoload.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rpv/inflector'
|
2
|
+
|
3
|
+
module RPV
|
4
|
+
module Autoload
|
5
|
+
def self.setup_paths(*paths)
|
6
|
+
paths = [paths].flatten.map { |p| File.expand_path(p) }
|
7
|
+
add_paths = paths.dup
|
8
|
+
paths.each do |base|
|
9
|
+
#trouble with directory named 'lib' inside jar, seems
|
10
|
+
#like it was not found by Dir[]
|
11
|
+
#puts "--- #{base}"
|
12
|
+
#puts Dir["#{base}/lib/**/*"]
|
13
|
+
Dir["#{base}/**/*"].each do |path|
|
14
|
+
if File.directory?(path)
|
15
|
+
add_paths << path
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
add_paths.each do |path|
|
20
|
+
$LOAD_PATH << path unless $LOAD_PATH.include?(path)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
#overwrite
|
27
|
+
class Class
|
28
|
+
def const_missing(sym)
|
29
|
+
begin
|
30
|
+
require sym.to_s.underscore
|
31
|
+
rescue LoadError => e
|
32
|
+
raise NameError.new("Missing constant #{sym.to_s} (#{e.to_s})")
|
33
|
+
end
|
34
|
+
sym.to_s.constantize
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rpv/wiring'
|
2
|
+
require 'rpv/method_helper'
|
3
|
+
require 'rpv/option_extractor'
|
4
|
+
|
5
|
+
module RPV
|
6
|
+
class BasePresenter
|
7
|
+
include MethodHelper
|
8
|
+
include OptionExtractor
|
9
|
+
|
10
|
+
attr_reader :model, :view
|
11
|
+
|
12
|
+
def self.wires(w = nil)
|
13
|
+
@wires = (@wires || []) << w unless w.nil?
|
14
|
+
@wires || []
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(*args)
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
def update_view(args = {})
|
22
|
+
update(:view, args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_model(args = {})
|
26
|
+
update(:model, args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_presenter(args = {})
|
30
|
+
update(:presenter, args)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def update(what, args = {})
|
35
|
+
what = what.to_s
|
36
|
+
self.class.wires.select { |w| w.send("#{what}_match", args) }.each do |w|
|
37
|
+
w.send("update_#{what}", @model, @view, self)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module RPV
|
2
|
+
# This module is used to publish events.
|
3
|
+
# Usage:
|
4
|
+
# class Foo < SomeThing
|
5
|
+
# include RPV::EventPublisher
|
6
|
+
#
|
7
|
+
# fires :some_event
|
8
|
+
# def bar
|
9
|
+
# fire :some_event
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# Now you can subscribe to the event:
|
14
|
+
# foo = Foo.new
|
15
|
+
# foo.subscribe :some_event do
|
16
|
+
# #do something
|
17
|
+
# end
|
18
|
+
# Or:
|
19
|
+
# foo.subscribe :some_event, RPV::Method.new(self, :some_method)
|
20
|
+
# Or (if you included RPV::MethodHelper):
|
21
|
+
# foo.subscribe :some_event, method(:some_method)
|
22
|
+
module EventPublisher
|
23
|
+
def self.included(base) #:nodoc:
|
24
|
+
base.extend ClassMethods
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
# Declare events.
|
29
|
+
# fires :some_event
|
30
|
+
# You can also declare several events at a time.
|
31
|
+
# fires :event_a, :event_b, :event_c
|
32
|
+
def fires(*events)
|
33
|
+
#TODO make inheritable?
|
34
|
+
@events ||= []
|
35
|
+
@events += [events].flatten.map { |e| e.to_sym }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Declare aliases for events.
|
39
|
+
# alias_event :some_event, :whatever
|
40
|
+
# Event receivers now can subscribe to the event :whatever.
|
41
|
+
# It's also possible to assign the same alias to multiple events.
|
42
|
+
# Aliases can only be used to subscribe to.
|
43
|
+
def alias_event(event, event_alias)
|
44
|
+
@event_aliases ||= Hash.new{|h,k| h[k] = []}
|
45
|
+
@event_aliases[event_alias.to_sym] << event.to_sym
|
46
|
+
end
|
47
|
+
|
48
|
+
# This method accepts regular expressions for aliasing events.
|
49
|
+
# Given there are following events:
|
50
|
+
# - ok_action_performed
|
51
|
+
# - cancel_action_performed
|
52
|
+
# alias_events /^(.*)_action_performed$/, "$1_clicked"
|
53
|
+
# Produces the aliases "ok_clicked" and "cancel_clicked"
|
54
|
+
# It can also be used to
|
55
|
+
# alias_events /^(.*)_action_performed$/, "some_button_clicked"
|
56
|
+
def alias_events(regex, alias_rule)
|
57
|
+
@events ||= []
|
58
|
+
@events.each do |event|
|
59
|
+
if match = regex.match(event.to_s)
|
60
|
+
alias_event event, alias_rule.gsub(/\$([0-9]+)/).each { |e| match[e[1..-1].to_i] }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns all registered events.
|
66
|
+
def events
|
67
|
+
@events || []
|
68
|
+
end
|
69
|
+
|
70
|
+
def event_aliases
|
71
|
+
@event_aliases || Hash.new{|h,k| h[k] = []}
|
72
|
+
end
|
73
|
+
|
74
|
+
def fires?(event)
|
75
|
+
events.include?(event) || event_aliases.keys.include?(event)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns all registered events.
|
80
|
+
def events
|
81
|
+
self.class.events
|
82
|
+
end
|
83
|
+
|
84
|
+
def event_aliases
|
85
|
+
self.class.event_aliases
|
86
|
+
end
|
87
|
+
|
88
|
+
# Subscribe to an event.
|
89
|
+
# method should be a RPV::Method or some object that responds to call(*args)
|
90
|
+
def subscribe(event, method = nil, &block)
|
91
|
+
@subscriptions ||= Hash.new{|h,k| h[k] = []}
|
92
|
+
if event.is_a?(String) || event.is_a?(Symbol)
|
93
|
+
if events.include? event
|
94
|
+
unless method.nil?
|
95
|
+
@subscriptions[event] << { :receiver => method }
|
96
|
+
else
|
97
|
+
@subscriptions[event] << { :receiver => block }
|
98
|
+
end
|
99
|
+
elsif event_aliases.keys.include? event
|
100
|
+
event_aliases[event].each do |event|
|
101
|
+
subscribe event, method, &block
|
102
|
+
end
|
103
|
+
end
|
104
|
+
elsif event.is_a?(Array)
|
105
|
+
event.uniq.each do |e|
|
106
|
+
subscribe(e, method, &block)
|
107
|
+
end
|
108
|
+
elsif event.is_a?(Regexp)
|
109
|
+
subscribe(events.find_all { |e| e.to_s =~ event }, method, &block)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
alias :when :subscribe
|
114
|
+
|
115
|
+
private
|
116
|
+
def fire(event, *args)
|
117
|
+
@subscriptions ||= Hash.new{|h,k| h[k] = []}
|
118
|
+
if @subscriptions.has_key? event
|
119
|
+
@subscriptions[event].each do |subscription|
|
120
|
+
subscription[:receiver].call *args
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rpv/generators/base'
|
2
|
+
|
3
|
+
module RPV
|
4
|
+
module Generators
|
5
|
+
class AppGenerator < Base
|
6
|
+
copy_all
|
7
|
+
|
8
|
+
create_dir 'app/presenters'
|
9
|
+
create_dir 'app/views'
|
10
|
+
create_dir 'app/models'
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@template_path = File.join(File.dirname(__FILE__), 'templates/app')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'rpv/inflector'
|
4
|
+
|
5
|
+
module RPV
|
6
|
+
module Generators
|
7
|
+
class Base
|
8
|
+
def self.describe
|
9
|
+
""
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.argument(name)
|
13
|
+
@argument_definitions ||= []
|
14
|
+
@argument_definitions << name
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.argument_definitions
|
18
|
+
@argument_definitions || []
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.copy_all
|
22
|
+
copy '**/*'
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.copy(glob = nil)
|
26
|
+
@copy = (@copy || []) << glob unless glob.nil?
|
27
|
+
@copy || []
|
28
|
+
end
|
29
|
+
|
30
|
+
# Adds a directory that should be created, although it is empty.
|
31
|
+
# Actually the directory does not need to exist in the template.
|
32
|
+
# Expects a relative path.
|
33
|
+
def self.create_dir(rel_path = nil)
|
34
|
+
@create_dir = (@create_dir || []) << rel_path unless rel_path.nil?
|
35
|
+
@create_dir || []
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def self.parse(rel_path, new_path = nil)
|
40
|
+
@templates ||= {}
|
41
|
+
@templates[rel_path] = new_path || rel_path.gsub(/\.erb$/, '')
|
42
|
+
copy rel_path
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.templates
|
46
|
+
@templates || {}
|
47
|
+
end
|
48
|
+
|
49
|
+
def execute(args = {})
|
50
|
+
to_path = File.join(FileUtils.pwd, args[:to_path]) #TODO
|
51
|
+
FileUtils.mkdir_p to_path
|
52
|
+
@arguments = args
|
53
|
+
parse_args
|
54
|
+
|
55
|
+
files = self.class.copy.map { |c| Dir.glob(File.join(@template_path, c)) }.flatten.uniq
|
56
|
+
|
57
|
+
files_new_path = {}
|
58
|
+
files.each do |file|
|
59
|
+
rel_path = relative_path(file, @template_path)
|
60
|
+
if self.class.templates.keys.member? rel_path
|
61
|
+
new_rel_path = eval("\"#{self.class.templates[rel_path]}\"")
|
62
|
+
files_new_path[File.join(to_path, new_rel_path)] = file
|
63
|
+
else
|
64
|
+
files_new_path[File.join(to_path, rel_path)] = file
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
files_new_path.keys.sort_by { |p| p.count('/') }.each do |new_path|
|
69
|
+
file = files_new_path[new_path]
|
70
|
+
rel_path = relative_path(new_path, to_path)
|
71
|
+
if File.directory?(file)
|
72
|
+
if File.exists?(new_path)
|
73
|
+
puts "exists #{rel_path}"
|
74
|
+
else
|
75
|
+
FileUtils.mkdir_p new_path
|
76
|
+
puts "create #{rel_path}"
|
77
|
+
end
|
78
|
+
else
|
79
|
+
exists = File.exists?(new_path)
|
80
|
+
if !exists || (exists && overwrite?(rel_path))
|
81
|
+
puts "create #{rel_path}"
|
82
|
+
if self.class.templates.keys.member? relative_path(file, @template_path)
|
83
|
+
FileUtils.mkdir_p(File.dirname(new_path)) unless File.exists?(File.dirname(new_path))
|
84
|
+
erb = ERB.new(File.read(file))
|
85
|
+
output = File.new(new_path, "w")
|
86
|
+
output << erb.result(binding)
|
87
|
+
output.close
|
88
|
+
else
|
89
|
+
FileUtils.cp file, new_path
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
self.class.create_dir.each do |d|
|
96
|
+
FileUtils.mkdir_p File.join(to_path, d)
|
97
|
+
puts "create dir #{d}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def overwrite?(path)
|
102
|
+
return true if @overwrite_all
|
103
|
+
puts "File #{path} exists. Overwrite? (y(es)/n(o)/a(ll)) "
|
104
|
+
a = STDIN.gets.downcase[0..0]
|
105
|
+
@overwrite_all = a == 'a'
|
106
|
+
@overwrite_all || a == 'y'
|
107
|
+
end
|
108
|
+
|
109
|
+
def method_missing(name, *args)
|
110
|
+
if @arguments.member? name
|
111
|
+
@arguments[name] rescue nil
|
112
|
+
else
|
113
|
+
raise NoMethodError, "no method #{name} on #{self.class.to_s}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def parse_args
|
118
|
+
end
|
119
|
+
|
120
|
+
# returns a relative path
|
121
|
+
# relative_path("/home/user/foo/bar.rb", "/home/user") #=> "foo/bar.rb"
|
122
|
+
# relative_path("/home/user/foo/bar.rb", "/home/user/") #=> "foo/bar.rb"
|
123
|
+
# relative_path("/home/user/code/home/foo/bar.rb", "/home") #=> "user/code/home/foo/bar.rb"
|
124
|
+
def relative_path(full_path, base_path)
|
125
|
+
rel = full_path.gsub(/^#{base_path}/, '')
|
126
|
+
rel.start_with?('/') ? rel[1..-1] : rel
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rpv/generators/base'
|
2
|
+
|
3
|
+
module RPV
|
4
|
+
module Generators
|
5
|
+
class PresenterGenerator < RPV::Generators::Base
|
6
|
+
argument :name
|
7
|
+
parse 'presenter.rb.erb', 'app/presenters/#{name}.rb'
|
8
|
+
parse 'presenter_spec.rb.erb', 'spec/presenters/#{name}_spec.rb'
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@template_path = File.dirname(__FILE__) + '/templates/presenter'
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse_args
|
15
|
+
@arguments[:name] = @arguments[:name].underscore + "_presenter"
|
16
|
+
@arguments[:class_name] = @arguments[:name].camelize
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.show?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.describe
|
24
|
+
"Creates a new presenter"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|