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.
Files changed (54) hide show
  1. data/Rakefile +3 -0
  2. data/bin/rpv +68 -0
  3. data/lib/rpv/autoload.rb +36 -0
  4. data/lib/rpv/base_presenter.rb +41 -0
  5. data/lib/rpv/event_publisher.rb +125 -0
  6. data/lib/rpv/generators/app_generator.rb +17 -0
  7. data/lib/rpv/generators/base.rb +130 -0
  8. data/lib/rpv/generators/presenter_generator.rb +28 -0
  9. data/lib/rpv/generators/templates/app/README +12 -0
  10. data/lib/rpv/generators/templates/app/Rakefile +3 -0
  11. data/lib/rpv/generators/templates/app/app/main.rb +4 -0
  12. data/lib/rpv/generators/templates/app/app/setup.rb +11 -0
  13. data/lib/rpv/generators/templates/app/spec/spec_helper.rb +5 -0
  14. data/lib/rpv/generators/templates/app/tasks/rspec.rake +9 -0
  15. data/lib/rpv/generators/templates/presenter/presenter.rb.erb +10 -0
  16. data/lib/rpv/generators/templates/presenter/presenter_spec.rb.erb +22 -0
  17. data/lib/rpv/inflector.rb +68 -0
  18. data/lib/rpv/method.rb +13 -0
  19. data/lib/rpv/method_helper.rb +9 -0
  20. data/lib/rpv/option_extractor.rb +15 -0
  21. data/lib/rpv/spec_helper.rb +118 -0
  22. data/lib/rpv/wiring/base.rb +49 -0
  23. data/lib/rpv/wiring/callable.rb +20 -0
  24. data/lib/rpv/wiring/converting.rb +36 -0
  25. data/lib/rpv/wiring/helpers.rb +23 -0
  26. data/lib/rpv/wiring/simple.rb +19 -0
  27. data/lib/rpv/wiring/unidirectional.rb +38 -0
  28. data/lib/rpv/wiring.rb +4 -0
  29. data/lib/rpv.rb +20 -0
  30. data/spec/autoload_spec.rb +24 -0
  31. data/spec/base_presenter_spec.rb +16 -0
  32. data/spec/event_publisher_spec.rb +121 -0
  33. data/spec/files/autoload/b/some_module.rb +2 -0
  34. data/spec/files/autoload/some_class.rb +3 -0
  35. data/spec/files/bar/presenter.rb.erb +5 -0
  36. data/spec/files/bar/view.rb.erb +3 -0
  37. data/spec/files/foo/Rakefile +0 -0
  38. data/spec/files/foo/app/main.rb.erb +1 -0
  39. data/spec/files/foo/app/models/templates/lala.html.erb +2 -0
  40. data/spec/generators/base_spec.rb +81 -0
  41. data/spec/generators/presenter_spec.rb +27 -0
  42. data/spec/method_helper_spec.rb +19 -0
  43. data/spec/method_spec.rb +12 -0
  44. data/spec/option_extractor_spec.rb +22 -0
  45. data/spec/spec_helper.rb +1 -0
  46. data/spec/spec_helper_spec.rb +15 -0
  47. data/spec/wiring/base_spec.rb +17 -0
  48. data/spec/wiring/callable_spec.rb +50 -0
  49. data/spec/wiring/helpers_spec.rb +29 -0
  50. data/spec/wiring/simple_spec.rb +47 -0
  51. data/spec/wiring/unidirectional_spec.rb +46 -0
  52. data/tasks/rdoc.rake +10 -0
  53. data/tasks/rspec.rake +10 -0
  54. metadata +118 -0
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ Dir.glob("tasks/**/*.rake").each do |rake_file|
2
+ load File.expand_path(File.dirname(__FILE__) + "/" + rake_file)
3
+ end
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
+
@@ -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