prepro 0.0.0 → 0.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.
@@ -0,0 +1,174 @@
1
+ # Abstract class with common features for Presenters. Use this for any objects that will be rendered
2
+ # in some shape or form. Use it for read only access, in other words, for index, show, new and edit
3
+ # actions in a rails app.
4
+ module Prepro
5
+ class Presenter
6
+
7
+ # Prepares collection of model instances or a single model instance for presentation
8
+ # @param[Integer, String(number), Hash, Model, Array<Model>] id_model_hash_collection id of model,
9
+ # attributes for new model, existing model or collection of models to present.
10
+ # @param[User, AnonymousUser] actor the actor who will view the model
11
+ # @param[ActionView::Base] view_context An instance of a view class. The default view class is
12
+ # ActionView::Base
13
+ # @param[Hash, optional] options
14
+ # @return[DecoratedModel, Array<DecoratedModel>] a model or collection thereof, decorated for
15
+ # presentation
16
+ def self.new(id_model_hash_collection, actor, view_context, options = {})
17
+ case id_model_hash_collection
18
+ when Array, ActiveRecord::Relation
19
+ present_collection(id_model_hash_collection, actor, view_context, options)
20
+ else
21
+ present_single(id_model_hash_collection, actor, view_context, options)
22
+ end
23
+ end
24
+
25
+ # Alias the basic access methods, so that they can be called for classes further down the
26
+ # inheritance chain, after another class overrode the method
27
+ # Aliasing class methods can only be done in the singleton method
28
+ # See: http://athikunte.blogspot.com/2008/03/aliasmethod-for-class-methods.html
29
+ class << self
30
+ alias_method :new_original, :new
31
+ end
32
+
33
+ # Prepares collection of model instances for presentation
34
+ # @param[Array<Model>] model_instances A collection of model instances
35
+ # @param[User, AnonymousUser] actor the actor who will view the model
36
+ # @param[ActionView::Base] view_context An instance of a view class. The default view class is
37
+ # ActionView::Base
38
+ # @param[Hash, optional] options
39
+ # @return[Array<DecoratedModel>] An array of models, each decorated for presentation
40
+ def self.present_collection(model_instances, actor, view_context, options = {})
41
+ presenter_attrs = OpenStruct.new(:actor => actor, :view_context => view_context, :options => options)
42
+ enforce_permissions(model_class.listable_by?(actor))
43
+ model_instances.each { |e| make_presentable!(e, presenter_attrs) }
44
+ model_instances
45
+ end
46
+
47
+ # Prepares a model instance for presentation
48
+ # @param[Integer, String(number), Model] id_hash_model id of model, attributes for model, or model
49
+ # to present
50
+ # @param[User, AnonymousUser] actor the actor who will view the model
51
+ # @param[ActionView::Base] view_context An instance of a view class. The default view class is
52
+ # ActionView::Base
53
+ # @param[Hash, optional] options
54
+ # @return[DecoratedModel] a model, decorated for presentation
55
+ def self.present_single(id_hash_model, actor, view_context, options = {})
56
+ presenter_attrs = OpenStruct.new(:actor => actor, :view_context => view_context, :options => options)
57
+ model_instance = load_model_instance(id_hash_model)
58
+ enforce_permissions(model_instance.viewable_by?(actor))
59
+ make_presentable!(model_instance, presenter_attrs)
60
+ model_instance
61
+ end
62
+
63
+ # Returns a model_instance, based on given id_hash_model
64
+ def self.load_model_instance(id_hash_model)
65
+ case id_hash_model
66
+ when Integer, /\A\d+/
67
+ model_class.find(id_hash_model)
68
+ when Hash
69
+ model_class.new(id_hash_model)
70
+ else
71
+ id_hash_model
72
+ end
73
+ end
74
+
75
+ # Override this in your concrete presenters with the class presented by self.
76
+ def self.model_class
77
+ raise "Implement me in concrete presenter"
78
+ end
79
+
80
+ # Override this in your concrete presenters with your own permission handling code.
81
+ def self.enforce_permissions(has_permission)
82
+ raise "Implement me in concrete presenter"
83
+ end
84
+
85
+ module DecoratorMixin
86
+
87
+ def presenter_attrs=(the_presenter_attrs)
88
+ @presenter_attrs = the_presenter_attrs
89
+ end
90
+
91
+ def presenter_attrs
92
+ @presenter_attrs
93
+ end
94
+
95
+ # Formats a_datetime
96
+ # @param[DateTime, Nil] a_datetime the datetime to format
97
+ # @param[Symbol] output_format the format to be applied: :distance_in_words,
98
+ # or any datetime format specified in initializers
99
+ def formatted_datetime(a_datetime, output_format, options = {})
100
+ return 'N/A' if a_datetime.blank?
101
+ case output_format
102
+ when :distance_in_words
103
+ if a_datetime < Time.now
104
+ # in the past
105
+ decorated_time_ago_in_words(a_datetime, options)
106
+ else
107
+ # in the future
108
+ decorated_time_from_now_in_words(a_datetime, options)
109
+ end
110
+ else
111
+ a_datetime.to_s(output_format)
112
+ end
113
+ end
114
+
115
+ # Renders time ago, showing absolute time on hover
116
+ # @param[DateTime] a_datetime the time to render
117
+ # @param[Hash, optional] options:
118
+ # * :suffix => printed after the time, default: ' ago'
119
+ # * :suppress_1 => if the number is a one, suppress it. Used for '... in the last month', which
120
+ # reads better than '... in the last 1 month'
121
+ # * :text_only => skip html tags
122
+ def decorated_time_ago_in_words(a_datetime, options = {})
123
+ options = {
124
+ :suffix => ' ago',
125
+ :suppress_1 => false,
126
+ :text_only => false
127
+ }.merge(options)
128
+ ts = ((presenter_attrs.view_context.time_ago_in_words(a_datetime).gsub('about ', '') + options[:suffix]) rescue 'N/A')
129
+ ts = ts.gsub(/^1\s+/, '') if options[:suppress_1]
130
+ if options[:text_only]
131
+ ts
132
+ else
133
+ presenter_attrs.view_context.content_tag(:span, ts, :title => a_datetime.to_s(:full_date_and_time))
134
+ end
135
+ end
136
+
137
+ # Renders time from now, showing absolute time on hover
138
+ # @param[DateTime] a_datetime the time to render
139
+ # @param[Hash, optional] options:
140
+ # * :prefix => printed before the time, default: 'in '
141
+ # * :suppress_1 => if the number is a one, suppress it. Used for '... in the last month', which
142
+ # reads better than '... in the last 1 month'
143
+ # * :text_only => skip html tags
144
+ def decorated_time_from_now_in_words(a_datetime, options = {})
145
+ options = {
146
+ :prefix => 'in ',
147
+ :suppress_1 => false,
148
+ :text_only => false
149
+ }.merge(options)
150
+ ts = (
151
+ (
152
+ options[:prefix] + presenter_attrs.view_context.time_ago_in_words(a_datetime).gsub('about ', '')
153
+ ) rescue 'N/A'
154
+ )
155
+ ts = ts.gsub(/^1\s+/, '') if options[:suppress_1]
156
+ if options[:text_only]
157
+ ts
158
+ else
159
+ presenter_attrs.view_context.content_tag(:span, ts, :title => a_datetime.to_s(:full_date_and_time))
160
+ end
161
+ end
162
+
163
+ def formatted_boolean(a_boolean)
164
+ a_boolean ? 'Yes' : 'No'
165
+ end
166
+
167
+ def indicate_blank
168
+ presenter_attrs.view_context.content_tag :span, "None Given", :class => 'label'
169
+ end
170
+
171
+ end
172
+
173
+ end
174
+ end
@@ -0,0 +1,90 @@
1
+ # Abstract class with common features for Processors. Use this for any objects that will receive
2
+ # attributes and will cause a persisted record to be changed or updated. In other words:
3
+ # Use it for the create, update, and destroy actions in a rails app.
4
+ module Prepro
5
+ class Processor
6
+
7
+ # Creates a new model_instance based on model_attrs.
8
+ # @param[Hash] model_attrs The attributes for the new model_instance. Includes DB columns,
9
+ # associated objects, nested attributes, etc.
10
+ # @param[User, AnonymousUser, Nil] actor The actor who creates the model_instance
11
+ # @return[Array<ModelInstance, Boolean>] A tuple with the newly created model_instance and a
12
+ # success flag.
13
+ def self.create(model_attrs, actor, options = {})
14
+ processor_attrs = OpenStruct.new(:attributes => model_attrs, :actor => actor, :options => options)
15
+ model_instance = model_class.new
16
+ enforce_permissions(model_instance.creatable_by?(actor))
17
+ before_assign_attributes_on_create(model_instance, processor_attrs)
18
+ # use this (instead of attributes=), once we're on Rails3.1: model_instance.assign_attributes(model_attrs, :as => options[:as])
19
+ model_instance.attributes = model_attrs
20
+ before_save_on_create(model_instance, processor_attrs)
21
+ success = model_instance.save
22
+ [model_instance, success]
23
+ end
24
+
25
+ # Updates an existing model_instance based on model_attrs.
26
+ # @param[Hash] model_attrs The attributes for the updated model_instance. Includes DB columns,
27
+ # the model's id, associated objects, nested attributes, etc.
28
+ # @param[User, AnonymousUser, Nil] actor The actor who updates the model_instance
29
+ # @return[Array<ModelInstance, Boolean>] A tuple with the updated model_instance and a success flag.
30
+ def self.update(model_attrs, actor, options = {})
31
+ processor_attrs = OpenStruct.new(:attributes => model_attrs, :actor => actor, :options => options)
32
+ model_instance = model_class.find(model_attrs[:id])
33
+ enforce_permissions(model_instance.updatable_by?(actor))
34
+ before_assign_attributes_on_update(model_instance, processor_attrs)
35
+ # use this (instead of attributes=), once we're on Rails3.1: model_instance.assign_attributes(model_attrs, :as => options[:as])
36
+ model_instance.attributes = model_attrs
37
+ before_save_on_update(model_instance, processor_attrs)
38
+ success = model_instance.save
39
+ [model_instance, success]
40
+ end
41
+
42
+ # Destroys an existing model_instance based on model_id
43
+ # @param[Integer, String<Number>] model_id The id of the model_instance to be destroyed
44
+ # @param[User, AnonymousUser, Nil] actor The actor who updates the model_instance
45
+ # @return[Array<ModelInstance, Boolean>] A tuple with the destroyed model_instance and a success flag.
46
+ def self.destroy(model_id, actor, options = {})
47
+ model_instance = model_class.find(model_id)
48
+ enforce_permissions(model_instance.destroyable_by?(actor))
49
+ model_instance.destroy
50
+ [model_instance, true]
51
+ end
52
+
53
+ # Alias the basic access methods, so that they can be called for classes further down the
54
+ # inheritance chain, after another class overrode the method
55
+ # Aliasing class methods can only be done in the singleton method
56
+ # See: http://athikunte.blogspot.com/2008/03/aliasmethod-for-class-methods.html
57
+ class << self
58
+ alias_method :create_original, :create
59
+ alias_method :update_original, :update
60
+ end
61
+
62
+ private
63
+
64
+ def self.before_assign_attributes_on_create(model_instance, processor_attrs)
65
+ end
66
+
67
+ def self.before_save_on_create(model_instance, processor_attrs)
68
+ end
69
+
70
+ def self.before_assign_attributes_on_update(model_instance, processor_attrs)
71
+ end
72
+
73
+ def self.before_save_on_update(model_instance, processor_attrs)
74
+ end
75
+
76
+ def self.model_class
77
+ raise "Implement me in concrete processor"
78
+ end
79
+
80
+ # Override this in your concrete processors with your own permission handling code.
81
+ def self.enforce_permissions(has_permission)
82
+ raise "Implement me in concrete processor"
83
+ end
84
+
85
+ def self.make_processable(model_instance, processor_attrs)
86
+ # nothing to do here, override in specific processors
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,3 @@
1
+ module Prepro
2
+ VERSION = "0.0.2"
3
+ end
data/lib/prepro.rb CHANGED
@@ -1,3 +1,5 @@
1
- # Shell for the Prepro gem
2
- class Prepro
3
- end
1
+ require 'prepro/presenter'
2
+ require 'prepro/processor'
3
+
4
+ module Prepro
5
+ end
data/readme.textile ADDED
@@ -0,0 +1,4 @@
1
+ Prepro gives your Rails app presenters and processors to slim down both controllers and models.
2
+
3
+ This is still a work in progress. I use it for production, however the API might change and it
4
+ requires a lot more documentation.
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prepro
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 0
10
- version: 0.0.0
9
+ - 2
10
+ version: 0.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jo Hund
@@ -15,11 +15,11 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-12-20 00:00:00 -08:00
18
+ date: 2012-01-25 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
22
- description: Presenters and Processors to make Rails apps more SOLID.
22
+ description: Presenters and Processors for clean Rails apps.
23
23
  email: jhund@clearcove.ca
24
24
  executables: []
25
25
 
@@ -29,6 +29,10 @@ extra_rdoc_files: []
29
29
 
30
30
  files:
31
31
  - lib/prepro.rb
32
+ - lib/prepro/presenter.rb
33
+ - lib/prepro/processor.rb
34
+ - lib/prepro/version.rb
35
+ - readme.textile
32
36
  has_rdoc: true
33
37
  homepage: http://rubygems.org/gems/prepro
34
38
  licenses: []
@@ -62,6 +66,6 @@ rubyforge_project:
62
66
  rubygems_version: 1.5.2
63
67
  signing_key:
64
68
  specification_version: 3
65
- summary: Presenters and Processors to make Rails apps more SOLID.
69
+ summary: Presenters and Processors for clean Rails apps.
66
70
  test_files: []
67
71