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
@@ -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