rpv_core 0.2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,12 @@
1
+ So you created a new RPV app. Here is a short guide to get started:
2
+
3
+ - Add your favorite GUI framework (at the time of writing there just is rpv_swing)
4
+ - add "require 'rpv/swing'" to app/setup.rb
5
+ - Check out the generators:
6
+ - run "rpv g" (or "jruby -S rpv g") in your application's directory to see what generators there are
7
+ - Create a presenter and a view.
8
+ - Make sure your app/main.rb creates an instance of your presenter.
9
+ - Run your app with "ruby app/main.rb" (or "jruby app/main.rb" if you use rpv_swing)
10
+ - "rpv r" serves as a shortcut for that
11
+
12
+ TODO: link to GH wiki?
@@ -0,0 +1,3 @@
1
+ Dir.glob("tasks/**/*.rake").each do |rake_file|
2
+ load File.expand_path(File.join(File.dirname(__FILE__), rake_file))
3
+ end
@@ -0,0 +1,4 @@
1
+ #this file is used to start your application
2
+ require File.join(File.dirname(__FILE__), 'setup')
3
+
4
+ #MainPresenter.new
@@ -0,0 +1,11 @@
1
+ AppBase = File.expand_path(File.dirname(__FILE__)) unless defined?(AppBase)
2
+ #AppBase contains the to .../your_application/app
3
+
4
+ require 'rubygems'
5
+ require 'rpv'
6
+
7
+ #autoload:
8
+ require 'rpv/autoload'
9
+ RPV::Autoload.setup_paths AppBase
10
+
11
+ #this is the right place to require any libs your application needs
@@ -0,0 +1,5 @@
1
+ require 'spec'
2
+ require 'spec/autorun'
3
+
4
+ Spec::Runner.configure do |config|
5
+ end
@@ -0,0 +1,9 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+
4
+ desc "Run all specs"
5
+ Spec::Rake::SpecTask.new('spec') do |t|
6
+ t.ruby_opts = ["-r app/setup"]
7
+ t.spec_opts = ["--colour", "--format progress", "--loadby mtime"]
8
+ t.spec_files = FileList['spec/**/*_spec.rb']
9
+ end
@@ -0,0 +1,10 @@
1
+ require 'rpv/base_presenter'
2
+
3
+ class <%= class_name %> < RPV::BasePresenter
4
+
5
+
6
+ def initialize(*args)
7
+ @view = extract_options(args, :view) || MyView.new
8
+
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper.rb"
2
+
3
+ describe <%= class_name %> do
4
+ describe "event handling" do
5
+ before(:all) do
6
+ @view = mock_event_publisher { |m|
7
+ #m.fires :some_event
8
+ }
9
+ @presenter = <%= class_name %>.new(:view => @view)
10
+ end
11
+
12
+
13
+ end
14
+
15
+ describe "setup" do
16
+ before(:each) do
17
+ @view = mock("view", :null_object => true)
18
+ end
19
+
20
+
21
+ end
22
+ end
@@ -0,0 +1,68 @@
1
+ # This code is a modified version of the Inflector class
2
+ # from the Ruby on Rails project (http://www.rubyonrails.com)
3
+
4
+ module RPV
5
+ module Inflector
6
+ # The reverse of +camelize+. Makes an underscored form from the expression in the string.
7
+ #
8
+ # Changes '::' to '/' to convert namespaces to paths.
9
+ #
10
+ # Examples
11
+ # "ActiveRecord".underscore #=> "active_record"
12
+ # "ActiveRecord::Errors".underscore #=> active_record/errors
13
+ def underscore()
14
+ self.to_s.gsub(/::/, '/').
15
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
16
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
17
+ tr("-", "_").
18
+ downcase
19
+ end
20
+
21
+ # Constantize tries to find a declared constant with the name specified
22
+ # in the string. It raises a NameError when the name is not in CamelCase
23
+ # or is not initialized.
24
+ #
25
+ # Examples
26
+ # "Module".constantize #=> Module
27
+ # "Class".constantize #=> Class
28
+ def constantize()
29
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self.to_s
30
+ raise NameError, "#{self.inspect} is not a valid constant name!"
31
+ end
32
+
33
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
34
+ end
35
+
36
+ # By default, camelize converts strings to UpperCamelCase. If the argument to camelize
37
+ # is set to ":lower" then camelize produces lowerCamelCase.
38
+ #
39
+ # camelize will also convert '/' to '::' which is useful for converting paths to namespaces
40
+ #
41
+ # Examples
42
+ # "active_record".camelize #=> "ActiveRecord"
43
+ # "active_record".camelize(:lower) #=> "activeRecord"
44
+ # "active_record/errors".camelize #=> "ActiveRecord::Errors"
45
+ # "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
46
+ def camelize(first_letter_in_uppercase = true)
47
+ if first_letter_in_uppercase
48
+ self.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
49
+ else
50
+ self.to_s[0..0] + camelize(self.to_s)[1..-1]
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ class String
57
+ include RPV::Inflector
58
+ end
59
+
60
+ class Symbol
61
+ include RPV::Inflector
62
+ end
63
+
64
+ class Class
65
+ def constantize
66
+ self
67
+ end
68
+ end
data/lib/rpv/method.rb ADDED
@@ -0,0 +1,13 @@
1
+ module RPV
2
+ class Method
3
+ attr_reader :object, :method
4
+
5
+ def initialize(object, method)
6
+ @object, @method = object, method
7
+ end
8
+
9
+ def call(*args)
10
+ @object.send(@method, *args)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ require 'rpv/method'
2
+
3
+ module RPV
4
+ module MethodHelper
5
+ def method(name)
6
+ RPV::Method.new(self, name.to_sym)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ module RPV
2
+ module OptionExtractor
3
+ def extract_options(args, *names)
4
+ args = args.find { |e| e.is_a?(Hash) } || {}
5
+ values = names.map do |name|
6
+ args[name]
7
+ end
8
+ if values.size == 1
9
+ values.first
10
+ else
11
+ values
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,118 @@
1
+ require 'rpv/event_publisher'
2
+ require 'spec/mocks'
3
+
4
+ class RPV::MockEventPublisher
5
+ include RPV::EventPublisher
6
+
7
+ # A wrapper for the private method 'fire'
8
+ def do_fire(event, *args)
9
+ fire event, *args
10
+ end
11
+
12
+ # Ignore missing methods
13
+ def method_missing(sym, *args)
14
+ Spec::Mocks::Mock.new("null object from MockEventPublisher#{sym.to_s}", :null_object => true)
15
+ end
16
+ end
17
+
18
+ def mock_event_publisher
19
+ m = Class.new(RPV::MockEventPublisher)
20
+ yield m if block_given?
21
+ m.new
22
+ end
23
+
24
+ def mock_block(*args)
25
+ yield m = mock(args)
26
+ m
27
+ end
28
+
29
+ Spec::Matchers.define :fire_event do |event|
30
+ match do |actual|
31
+ actual.fires?(event.to_sym)
32
+ end
33
+
34
+ failure_message_for_should do |actual|
35
+ "expected #{actual} to fire #{event}"
36
+ end
37
+
38
+ failure_message_for_should_not do |actual|
39
+ "expected #{actual} not to fire #{event}"
40
+ end
41
+
42
+ description do
43
+ "fire #{event}"
44
+ end
45
+ end
46
+
47
+ Spec::Matchers.define :fire_events do |*events|
48
+ match do |actual|
49
+ events.flatten.each do |event|
50
+ actual.should fire_event event
51
+ end
52
+ end
53
+
54
+ description do
55
+ "fire #{events.join(', ')}"
56
+ end
57
+ end
58
+
59
+ Spec::Matchers.define :have_attr_reader do |attr|
60
+ match do |actual|
61
+ actual.respond_to?(attr)
62
+ end
63
+
64
+ failure_message_for_should do |actual|
65
+ "expected #{actual} to have an attribute reader for #{attr}"
66
+ end
67
+
68
+ failure_message_for_should_not do |actual|
69
+ "expected #{actual} not to have an attribute reader for #{attr}"
70
+ end
71
+
72
+ description do
73
+ "have an attribute reader for #{attr}"
74
+ end
75
+ end
76
+
77
+ Spec::Matchers.define :have_attr_accessor do |attr|
78
+ match do |actual|
79
+ actual.respond_to?(attr) && actual.respond_to?("#{attr}=")
80
+ end
81
+
82
+
83
+ failure_message_for_should do |actual|
84
+ "expected #{actual} to have an attribute accessor for #{attr}"
85
+ end
86
+
87
+ failure_message_for_should_not do |actual|
88
+ "expected #{actual} not to have an attribute accessor for #{attr}"
89
+ end
90
+
91
+ description do
92
+ "have an attribute accessor for #{attr}"
93
+ end
94
+ end
95
+
96
+ Spec::Matchers.define :have_attr_accessors do |*attrs|
97
+ match do |actual|
98
+ attrs.flatten.each do |attr|
99
+ actual.should have_attr_accessor attr
100
+ end
101
+ end
102
+
103
+ description do
104
+ "have attribute accessors for #{attrs.join(', ')}"
105
+ end
106
+ end
107
+
108
+ Spec::Matchers.define :have_attr_readers do |*attrs|
109
+ match do |actual|
110
+ attrs.flatten.each do |attr|
111
+ actual.should have_attr_reader attr
112
+ end
113
+ end
114
+
115
+ description do
116
+ "have attribute readers for #{attrs.join(', ')}"
117
+ end
118
+ end
@@ -0,0 +1,49 @@
1
+ module RPV
2
+ module Wiring
3
+ class Base
4
+ def initialize(args = {})
5
+ @model, @view, @presenter = args[:model], args[:view], args[:presenter]
6
+ raise ArgumentError, "View field must be given" if @view.nil? || (!@model.nil? && !@presenter.nil?)
7
+ end
8
+
9
+ # Match the view method against a hash of constraints (like { :only => /^bar\.(.*)/, :except => /(.*)\.text$/ })
10
+ def view_match(args = {})
11
+ match(@view, args)
12
+ end
13
+
14
+ # Match the presenter method against a hash of constraints (like { :only => /^bar\.(.*)/, :except => /(.*)\.text$/ })
15
+ def presenter_match(args = {})
16
+ match(@presenter, args)
17
+ end
18
+
19
+ # Match the model method against a hash of constraints (like { :only => /^bar\.(.*)/, :except => /(.*)\.text$/ })
20
+ def model_match(args = {})
21
+ match(@model, args)
22
+ end
23
+
24
+ def update_view(model, view, presenter)
25
+ end
26
+
27
+ def update_model(model, view, presenter)
28
+ end
29
+
30
+ def update_presenter(model, view, presenter)
31
+ end
32
+
33
+ protected
34
+ def match(method, args)
35
+ !method.nil? && (args[:only].nil? || method =~ args[:only]) && (args[:except].nil? || !(method =~ args[:except]))
36
+ end
37
+
38
+ def send_nested(obj, methods_string, *args)
39
+ methods = methods_string.to_s.split('.')
40
+ if methods.size > 1
41
+ first = methods.slice! 0
42
+ send_nested(obj.send(first), methods.join('.'), *args)
43
+ else
44
+ obj.send(methods.first, *args)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ require 'rpv/wiring/converting'
2
+
3
+ module RPV
4
+ module Wiring
5
+ class Callable < Converting
6
+ def initialize(*args)
7
+ super
8
+ @to_block, @from_block = args[0][:to_view], args[0][:from_view]
9
+ end
10
+
11
+ def convert_to_view(value)
12
+ @to_block.call(value)
13
+ end
14
+
15
+ def convert_from_view(value)
16
+ @from_block.call(value)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,36 @@
1
+ require 'rpv/wiring/base'
2
+
3
+ module RPV
4
+ module Wiring
5
+ # Abstract
6
+ class Converting < Base
7
+ def initialize(args)
8
+ super
9
+ end
10
+
11
+ def update_view(model, view, presenter)
12
+ unless @model.nil?
13
+ send_nested(view, "#{@view}=", convert_to_view(send_nested(model, @model)))
14
+ else
15
+ send_nested(view, "#{@view}=", convert_to_view(send_nested(presenter, @presenter)))
16
+ end
17
+ end
18
+
19
+ def update_model(model, view, presenter)
20
+ send_nested(model, "#{@model}=", convert_from_view(send_nested(view, @view))) unless @model.nil?
21
+ end
22
+
23
+ def update_presenter(model, view, presenter)
24
+ send_nested(presenter, "#{@presenter}=", convert_from_view(send_nested(view, @view))) unless @presenter.nil?
25
+ end
26
+
27
+ def convert_from_view(value)
28
+ nil
29
+ end
30
+
31
+ def convert_to_view(value)
32
+ nil
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,23 @@
1
+ module RPV
2
+ module Wiring
3
+ module WireHelpers
4
+ module ClassMethods
5
+ def simple(*args)
6
+ Simple.new(*args)
7
+ end
8
+
9
+ def unidirectional(*args)
10
+ Unidirectional.new(*args)
11
+ end
12
+
13
+ def callable(*args)
14
+ Callable.new(*args)
15
+ end
16
+ end
17
+
18
+ def self.included(base)
19
+ base.extend(ClassMethods)
20
+ end
21
+ end
22
+ end
23
+ end