hammock 0.2.11.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 (47) hide show
  1. data/History.txt +67 -0
  2. data/LICENSE +24 -0
  3. data/Manifest.txt +46 -0
  4. data/README.rdoc +105 -0
  5. data/Rakefile +29 -0
  6. data/hammock.gemspec +43 -0
  7. data/lib/hammock/ajaxinate.rb +154 -0
  8. data/lib/hammock/callback.rb +16 -0
  9. data/lib/hammock/callbacks.rb +94 -0
  10. data/lib/hammock/canned_scopes.rb +125 -0
  11. data/lib/hammock/constants.rb +9 -0
  12. data/lib/hammock/controller_attributes.rb +52 -0
  13. data/lib/hammock/export_scope.rb +74 -0
  14. data/lib/hammock/hamlink_to.rb +47 -0
  15. data/lib/hammock/javascript_buffer.rb +63 -0
  16. data/lib/hammock/logging.rb +98 -0
  17. data/lib/hammock/model_attributes.rb +53 -0
  18. data/lib/hammock/model_logging.rb +30 -0
  19. data/lib/hammock/monkey_patches/action_pack.rb +32 -0
  20. data/lib/hammock/monkey_patches/active_record.rb +231 -0
  21. data/lib/hammock/monkey_patches/array.rb +73 -0
  22. data/lib/hammock/monkey_patches/hash.rb +57 -0
  23. data/lib/hammock/monkey_patches/logger.rb +28 -0
  24. data/lib/hammock/monkey_patches/module.rb +27 -0
  25. data/lib/hammock/monkey_patches/numeric.rb +25 -0
  26. data/lib/hammock/monkey_patches/object.rb +61 -0
  27. data/lib/hammock/monkey_patches/route_set.rb +28 -0
  28. data/lib/hammock/monkey_patches/string.rb +197 -0
  29. data/lib/hammock/overrides.rb +36 -0
  30. data/lib/hammock/resource_mapping_hooks.rb +28 -0
  31. data/lib/hammock/resource_retrieval.rb +119 -0
  32. data/lib/hammock/restful_actions.rb +172 -0
  33. data/lib/hammock/restful_rendering.rb +114 -0
  34. data/lib/hammock/restful_support.rb +186 -0
  35. data/lib/hammock/route_drawing_hooks.rb +22 -0
  36. data/lib/hammock/route_for.rb +59 -0
  37. data/lib/hammock/route_node.rb +159 -0
  38. data/lib/hammock/route_step.rb +87 -0
  39. data/lib/hammock/scope.rb +127 -0
  40. data/lib/hammock/suggest.rb +36 -0
  41. data/lib/hammock/utils.rb +42 -0
  42. data/lib/hammock.rb +29 -0
  43. data/misc/scaffold.txt +83 -0
  44. data/misc/template.rb +17 -0
  45. data/tasks/hammock_tasks.rake +5 -0
  46. data/test/hammock_test.rb +8 -0
  47. metadata +142 -0
@@ -0,0 +1,186 @@
1
+ module Hammock
2
+ module RestfulSupport
3
+ def self.included base # :nodoc:
4
+ base.send :include, InstanceMethods
5
+ base.send :extend, ClassMethods
6
+
7
+ base.class_eval {
8
+ before_modify :set_editing
9
+ # TODO Investigate the usefulness of this.
10
+ # before_destroy :set_editing
11
+ before_create :set_creator_id_if_appropriate
12
+ before_create :set_new_or_deleted_before_save
13
+ before_undestroy :set_new_or_deleted_before_save
14
+ helper_method :mdl, :mdl_name, :editing?, :nested_within?, :partial_exists?
15
+ }
16
+ end
17
+
18
+ module ClassMethods
19
+
20
+ # The model this controller operates on. Defined as the singularized controller name. For example, for +GelatinousBlobsController+, this will return the +GelatinousBlob+ class.
21
+ def mdl
22
+ @hammock_cached_mdl ||= Object.const_get to_s.sub('Controller', '').classify
23
+ end
24
+ # The lowercase name of the model this controller operates on. For example, for +GelatinousBlobsController+, this will return "gelatinous_blob".
25
+ def mdl_name
26
+ @hammock_cached_mdl_name ||= to_s.sub('Controller', '').singularize.underscore
27
+ end
28
+
29
+ end
30
+
31
+ module InstanceMethods
32
+ private
33
+
34
+ # The model this controller operates on. Defined as the singularized controller name. For example, for +GelatinousBlobsController+, this will return the +GelatinousBlob+ class.
35
+ def mdl
36
+ self.class.mdl
37
+ end
38
+ # The lowercase name of the model this controller operates on. For example, for +GelatinousBlobsController+, this will return "gelatinous_blob".
39
+ def mdl_name
40
+ self.class.mdl_name
41
+ end
42
+
43
+ # Returns the node in the Hammock routing map corresponding to the (possibly nested) resource handling the current request.
44
+ def current_hammock_resource
45
+ nesting_resources = params.keys.select {|k| /_id$/ =~ k }.map {|k| k.gsub(/_id$/, '').pluralize }
46
+ ActionController::Routing::Routes.route_map.base_for nesting_resources.push(mdl.resource_name).map(&:to_sym)
47
+ end
48
+
49
+ # Returns true if the current action represents an edit on +record+.
50
+ #
51
+ # For example, consider the route <tt>/articles/3/comments/31/edit</tt>, which fires <tt>CommentsController#edit</tt>. The nested route handler would assign <tt>@comment</tt> and <tt>@article</tt> to the appropriate records, and then the following would be observed:
52
+ # editing?(@comment) #=> true
53
+ # editing?(@article) #=> false
54
+ def editing? record
55
+ record == @editing
56
+ end
57
+
58
+ # Returns <tt>params[key]</tt>, defaulting to an empty Hash if <tt>params[key]</tt> can't receive :[].
59
+ #
60
+ # This is useful for concise nested parameter access. For example, if <tt>params[:account]</tt> is nil:
61
+ # params[:account][:email] #=> NoMethodError: undefined method `[]' for nil:NilClass
62
+ # params_for(:account)[:email] #=> nil
63
+ def params_for key
64
+ params[key] || {}
65
+ end
66
+
67
+ def assign_entity record_or_records
68
+ @entity = if record_or_records.nil?
69
+ # Fail
70
+ elsif record_or_records.is_a? ActiveRecord::Base
71
+ instance_variable_set "@#{mdl_name}", (@record = record_or_records)
72
+ elsif record_or_records.is_a? Ambition::Context
73
+ log "Unkicked query: #{record_or_records.to_hash.inspect}"
74
+ instance_variable_set "@#{mdl_name.pluralize}", (@records = record_or_records)
75
+ elsif record_or_records.is_a? Array
76
+ instance_variable_set "@#{mdl_name.pluralize}", (@records = record_or_records)
77
+ else
78
+ raise "Unknown record(s) type #{record_or_records.class}."
79
+ end
80
+
81
+ @entity if assign_nesting_entities
82
+ end
83
+
84
+ private
85
+
86
+ def assign_nesting_entities
87
+ results = nesting_scope_list.map &:kick
88
+
89
+ if results.all? {|records| records.length == 1 }
90
+ results.map {|records|
91
+ records.first
92
+ }.all? {|record|
93
+ log "nested record #{record.concise_inspect} assigned to @#{record.base_model}."
94
+ add_nested_entity record
95
+ instance_variable_set "@#{record.base_model}", record
96
+ }
97
+ end
98
+ end
99
+
100
+ def make_new_record resource = mdl
101
+ resource.new_with(params_for(resource.symbolize))
102
+ end
103
+
104
+ def assign_createable
105
+ assign_entity make_createable
106
+ end
107
+
108
+ def make_createable resource = mdl
109
+ if !(new_record = make_new_record(resource))
110
+ log "Couldn't create a new #{resource.base_model} with the given nesting level and parameters."
111
+ elsif !new_record.createable_by?(current_user)
112
+ log "#{requester_name} can't create #{new_record.resource_name}."
113
+ else
114
+ new_record
115
+ end
116
+ end
117
+
118
+ def current_nested_records
119
+ (@current_nested_records || []).dup
120
+ end
121
+
122
+ def current_nested_resources
123
+ (@current_nested_resources || []).dup
124
+ end
125
+
126
+ def add_nested_entity entity
127
+ (@current_nested_records ||= []).push entity
128
+ (@current_nested_resources ||= []).push entity.resource
129
+ end
130
+
131
+ def nested_within? record_or_resource
132
+ if record_or_resource.is_a? ActiveRecord::Base
133
+ current_nested_records.include? record_or_resource
134
+ else
135
+ current_nested_resources.include? record_or_resource
136
+ end
137
+ end
138
+
139
+ def safe_verb_and_implication?
140
+ request.get? && !action_name.to_s.in?(Hammock::Constants::ImpliedUnsafeActions)
141
+ end
142
+
143
+ def set_editing
144
+ @editing = @record
145
+ end
146
+
147
+ def set_new_or_deleted_before_save
148
+ @record.set_new_or_deleted_before_save
149
+ end
150
+
151
+ # TODO process /^creating_\w+_id$/ as well
152
+ def set_creator_id_if_appropriate
153
+ if @record.respond_to? :creator_id=
154
+ if current_user.nil?
155
+ log "Warning: @#{@record.base_model}.creator_id isn't being set, since current_user was nil."
156
+ else
157
+ @record.creator_id = current_user.id
158
+ end
159
+ end
160
+ end
161
+
162
+ def partial_exists? name, format = nil
163
+ partial_name, ctrler_name = name.split('/', 2).reverse
164
+ !Dir.glob(File.join(
165
+ RAILS_ROOT,
166
+ 'app/views',
167
+ ctrler_name || '',
168
+ "_#{partial_name}.#{format || request.format.to_sym.to_s}.*"
169
+ )).empty?
170
+ end
171
+
172
+ def redirect_back_or opts = {}, *parameters_for_method_reference
173
+ if request.referer.blank?
174
+ redirect_to opts, *parameters_for_method_reference
175
+ else
176
+ redirect_to request.referer
177
+ end
178
+ end
179
+
180
+ def rendered_or_redirected?
181
+ @performed_render || @performed_redirect
182
+ end
183
+
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,22 @@
1
+ module Hammock
2
+ module RouteDrawingHooks
3
+ MixInto = ActionController::Routing::RouteSet
4
+
5
+ def self.included base # :nodoc:
6
+ base.send :include, Methods
7
+
8
+ base.class_eval {
9
+ alias_method_chain_once :draw, :hammock_route_map_init
10
+ }
11
+ end
12
+
13
+ module Methods
14
+
15
+ def draw_with_hammock_route_map_init &block
16
+ ActionController::Routing::Routes.send :initialize_hammock_route_map
17
+ draw_without_hammock_route_map_init &block
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,59 @@
1
+ module Hammock
2
+ module RouteFor
3
+ def self.included base # :nodoc:
4
+ base.send :include, InstanceMethods
5
+ base.send :extend, ClassMethods
6
+
7
+ base.class_eval {
8
+ helper_method :path_for, :nested_path_for, :route_for, :nested_route_for
9
+ }
10
+ end
11
+
12
+ module ClassMethods
13
+ end
14
+
15
+ module InstanceMethods
16
+ private
17
+
18
+ def path_for *args
19
+ route_for(*args).path
20
+ end
21
+
22
+ def nested_path_for *args
23
+ nested_route_for(*args).path
24
+ end
25
+
26
+ def route_for *args
27
+ args.compact!
28
+ opts = args.extract_options!
29
+ verb = args.shift if args.first.is_a?(Symbol)
30
+
31
+ ActionController::Routing::Routes.route_map.for verb_for(verb, args.last), args, opts
32
+ end
33
+
34
+ def verb_for requested_verb, record
35
+ requested_verb = :show if requested_verb.blank?
36
+
37
+ if (:show == requested_verb) && record.is_a?(Class)
38
+ :index
39
+ elsif :modify == requested_verb
40
+ record.new_record? ? :new : :edit
41
+ elsif :save == requested_verb
42
+ record.new_record? ? :create : :update
43
+ else
44
+ requested_verb
45
+ end
46
+ end
47
+
48
+ def nested_route_for *resources
49
+ resources.delete_if &:nil?
50
+ requested_verb = resources.shift if resources.first.is_a?(Symbol)
51
+ args = current_nested_records.concat(resources)
52
+
53
+ args.unshift(requested_verb) unless requested_verb.nil?
54
+ route_for *args
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,159 @@
1
+ module Hammock
2
+ class RouteNode < ActionController::Resources::Resource
3
+
4
+ DefaultRecordVerbs = {
5
+ :show => :get,
6
+ :edit => :get,
7
+ :update => :put,
8
+ :destroy => :delete
9
+ }.freeze
10
+ DefaultResourceVerbs = {
11
+ :index => :get
12
+ }.freeze
13
+ DefaultBuildVerbs = {
14
+ :new => :get,
15
+ :create => :post
16
+ }.freeze
17
+
18
+ attr_reader :mdl, :resource, :parent, :children, :routing_parent, :record_routes, :resource_routes, :build_routes
19
+
20
+ def initialize entity = nil, options = {}
21
+ @parent = options[:parent]
22
+ @children = {}
23
+ unless root?
24
+ @mdl = entity if entity.is_a?(Symbol)
25
+ @routing_parent = determine_routing_parent
26
+ define_routes options
27
+ end
28
+ end
29
+
30
+ def resource
31
+ # TODO performance
32
+ Object.const_get mdl.to_s.classify rescue nil
33
+ end
34
+
35
+ def root?
36
+ parent.nil?
37
+ end
38
+
39
+ def ancestry
40
+ root? ? [] : parent.ancestry.push(self)
41
+ end
42
+
43
+ def for verb, entities, options
44
+ raise "Hammock::RouteNode#for requires an explicitly specified verb as its first argument." unless verb.is_a?(Symbol)
45
+ raise "Hammock::RouteNode#for requires an Array of at least one record or resource." if entities.empty? || !entities.is_a?(Array)
46
+
47
+ entity = entities.shift
48
+
49
+ if entities.empty?
50
+ steps_for verb, entity
51
+ else
52
+ children[entity.resource_sym].for(verb, entities, options).within steps_for(nil, entity)
53
+ end
54
+ end
55
+
56
+ def base_for resources
57
+ # puts "base_for<#{mdl}>: resources=#{resources.inspect}."
58
+ if resources.empty?
59
+ self
60
+ else
61
+ match = nil
62
+ children.values.detect {|child|
63
+ # puts " Trying #{child.ancestry.map(&:mdl).inspect} for #{resources.inspect}"
64
+ if !resources.include?(child.mdl)
65
+ # puts " Can't match #{resources.inspect} against #{child.mdl}."
66
+ nil
67
+ else
68
+ # puts " Matched #{child.mdl} from #{resources.inspect}."
69
+ match = child.base_for resources.discard(child.mdl)
70
+ end
71
+ } #|| raise("There is no routing path for #{resources.map(&:inspect).inspect}.")
72
+ match
73
+ end
74
+ end
75
+
76
+ def nesting_scope_list_for params
77
+ if root?
78
+ [ ]
79
+ else
80
+ parent.nesting_scope_list_for(params).push nesting_scope_segment_for(params)
81
+ end
82
+ end
83
+
84
+ def nesting_scope_segment_for params
85
+ raise "The root of the route map isn't associated with a resource." if root?
86
+ puts "is this undefined? #{resource.accessible_attributes_on_create.inspect}"
87
+ value = params.delete resource.param_key
88
+ puts "resource.select {|r| r.#{resource.routing_attribute} == value }"
89
+ eval "resource.select {|r| r.#{resource.routing_attribute} == value }"
90
+ end
91
+
92
+ def add entity, options, steps = nil
93
+ if steps.nil?
94
+ add entity, options, (options[:name_prefix] || '').chomp('_').split('_').map {|i| i.pluralize.underscore.to_sym }
95
+ elsif steps.empty?
96
+ add_child entity, options
97
+ else
98
+ children[steps.shift].add entity, options, steps
99
+ end
100
+ end
101
+
102
+ def routeable_as verb, entity
103
+ if entity.record? && record_routes[verb || :show]
104
+ :record
105
+ elsif entity.resource? && resource_routes[verb || :index]
106
+ :resource
107
+ elsif !verb.nil? && build_routes[verb]
108
+ :build
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def define_routes options
115
+ @record_routes = DefaultRecordVerbs.dup.update(options[:member] || {})
116
+ @resource_routes = DefaultResourceVerbs.dup.update(options[:collection] || {})
117
+ @build_routes = DefaultBuildVerbs.dup.update(options[:build] || {})
118
+ end
119
+
120
+ def determine_routing_parent
121
+ puts "\ndetermine_routing_parent: #{mdl}:"
122
+ if parent.nil? || parent.resource.nil?
123
+ puts "Resource for #{mdl} has either no parent or no resource - not nestable."
124
+ nil
125
+ else
126
+ puts "reflections: #{resource.reflections.keys.inspect}"
127
+ puts "nestable_routing_resources: #{resource.nestable_routing_resources.inspect}"
128
+ scannable_reflections = resource.nestable_routing_resources.nil? ? resource.reflections : resource.reflections.dragnet(*resource.nestable_routing_resources)
129
+ puts "scannable reflections: #{scannable_reflections.keys.inspect}"
130
+ valid_reflections = scannable_reflections.selekt {|k,v| puts "#{v.klass}<#{v.object_id}> == #{parent.resource}<#{parent.resource.object_id}> #=> #{v.klass == parent.resource}"; v.klass == parent.resource }
131
+ puts "valid reflections: #{valid_reflections.keys.inspect}"
132
+
133
+ if valid_reflections.keys.length < 1
134
+ raise "The routing table specifies that #{mdl} is nested within #{parent.mdl}, but there is no ActiveRecord association linking #{resource} to #{parent.resource}. Example: 'belongs_to :#{parent.resource.base_model}' in the #{resource} model."
135
+ elsif valid_reflections.keys.length > 1
136
+ raise "#{resource} defines more than one association to #{parent.resource} (#{valid_reflections.keys.map(&:to_s).join(', ')}). That's fine, but you need to use #{resource}.nest_within to specify the one Hammock should nest scopes through. For example, 'nest_within #{valid_reflections.keys.first.inspect}' in the #{resource} model."
137
+ end
138
+
139
+ valid_reflections.keys.first
140
+ end
141
+ end
142
+
143
+ def add_child entity, options
144
+ child = RouteNode.new entity, options.merge(:parent => self)
145
+ children[child.mdl] = child
146
+ end
147
+
148
+ def steps_for verb, entity
149
+ child = children[entity.resource_sym]
150
+
151
+ if child.nil?
152
+ raise "No routes are defined for #{entity.resource}#{' within ' + ancestry.map {|r| r.mdl.to_s }.join(', ') unless ancestry.empty?}."
153
+ else
154
+ RouteStep.new(child).for(verb, entity)
155
+ end
156
+ end
157
+
158
+ end
159
+ end
@@ -0,0 +1,87 @@
1
+ module Hammock
2
+ class RouteStep
3
+ attr_reader :resource, :routeable_as, :verb, :entity, :parent
4
+
5
+ def initialize resource
6
+ @resource = resource
7
+ end
8
+
9
+ def for verb, entity
10
+ routeable_as = resource.routeable_as(verb, entity)
11
+
12
+ if !routeable_as
13
+ raise "The verb '#{verb}' can't be applied to " + (entity.record? ? "#{entity.resource} records" : "the #{entity.resource} resource") + "."
14
+ elsif (:record == routeable_as) && entity.new_record?
15
+ raise "The verb '#{verb}' requires a #{entity.resource} with an ID (i.e. not a new record)."
16
+ elsif (:build == routeable_as) && entity.record? && !entity.new_record?
17
+ raise "The verb '#{verb}' requires either the #{entity.resource} resource, or a #{entity.resource} without an ID (i.e. a new record)."
18
+ else
19
+ @verb, @entity, @routeable_as = verb, entity, routeable_as
20
+ end
21
+
22
+ self
23
+ end
24
+
25
+ def within parent
26
+ @parent = parent
27
+ self
28
+ end
29
+
30
+ def setup?
31
+ !@entity.nil?
32
+ end
33
+
34
+ def path params = nil
35
+ raise_unless_setup_while_trying_to 'render a path'
36
+
37
+ buf = '/'
38
+ buf << entity.resource_name
39
+ buf << '/' + entity.to_param if entity.record? && !entity.new_record?
40
+ buf << '/' + verb.to_s unless verb.nil? or implied_verb?(verb)
41
+
42
+ buf = parent.path + buf unless parent.nil?
43
+ buf << param_str(params)
44
+
45
+ buf
46
+ end
47
+
48
+ def http_method
49
+ raise_unless_setup_while_trying_to 'extract the HTTP method'
50
+ resource.send("#{routeable_as}_routes")[verb]
51
+ end
52
+
53
+ def fake_http_method
54
+ http_method.in?(:get, :post) ? http_method : :post
55
+ end
56
+
57
+ def get?; :get == http_method end
58
+ def post?; :post == http_method end
59
+ def put?; :put == http_method end
60
+ def delete?; :delete == http_method end
61
+
62
+ def safe?
63
+ get? && !verb.in?(Hammock::Constants::ImpliedUnsafeActions)
64
+ end
65
+
66
+ private
67
+
68
+ def implied_verb? verb
69
+ verb.in? :index, :create, :show, :update, :destroy
70
+ end
71
+
72
+ def raise_unless_setup_while_trying_to task
73
+ raise "You have to call for(verb, entity) (and optionally within(parent)) on this RouteStep before you can #{task}." unless setup?
74
+ end
75
+
76
+ def param_str params
77
+ link_params = entity.record? ? entity.unsaved_attributes.merge(params || {}) : params
78
+
79
+ if link_params.blank?
80
+ ''
81
+ else
82
+ '?' + {entity.base_model => link_params}.to_query
83
+ end
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,127 @@
1
+ module Hammock
2
+ module Scope
3
+ def self.included base # :nodoc:
4
+ base.send :include, InstanceMethods
5
+ base.send :extend, ClassMethods
6
+
7
+ base.class_eval {
8
+ helper_method :can_verb_entity?
9
+ }
10
+ end
11
+
12
+ module ClassMethods
13
+ end
14
+
15
+ module InstanceMethods
16
+ private
17
+
18
+ def can_verb_entity? verb, entity
19
+ if entity.is_a? ActiveRecord::Base
20
+ can_verb_record? verb, entity
21
+ else
22
+ can_verb_resource? verb, entity
23
+ end == :ok
24
+ end
25
+
26
+ def can_verb_resource? verb, resource
27
+ raise "The verb at #{call_point} must be supplied as a Symbol." unless verb.nil? || verb.is_a?(Symbol)
28
+ route = route_for verb, resource
29
+ if route.safe? && !resource.indexable_by(current_user)
30
+ log "#{requester_name} can't index #{resource.name.pluralize}. #{describe_call_point 4}"
31
+ :not_found
32
+ elsif !route.safe? && !make_createable(resource)
33
+ log "#{requester_name} can't #{verb} #{resource.name.pluralize}. #{describe_call_point 4}"
34
+ :read_only
35
+ else
36
+ # log "#{requester_name} can #{verb} #{resource.name.pluralize}."
37
+ :ok
38
+ end
39
+ end
40
+
41
+ def can_verb_record? verb, record
42
+ raise "The verb at #{call_point} must be supplied as a Symbol." unless verb.nil? || verb.is_a?(Symbol)
43
+ route = route_for verb, record
44
+ if route.verb.in?(:save, :create) && record.new_record?
45
+ if !record.createable_by?(current_user)
46
+ log "#{requester_name} can't create a #{record.class} with #{record.attributes.inspect}. #{describe_call_point 4}"
47
+ :unauthed
48
+ else
49
+ :ok
50
+ end
51
+ else
52
+ if !record.readable_by?(current_user)
53
+ log "#{requester_name} can't see #{record.class}<#{record.id}>. #{describe_call_point 4}"
54
+ :not_found
55
+ elsif !route.safe? && !record.writeable_by?(current_user)
56
+ log "#{requester_name} can't #{verb} #{record.class}<#{record.id}>. #{describe_call_point 4}"
57
+ :read_only
58
+ else
59
+ # log "#{requester_name} can #{verb} #{record.class}<#{record.id}>."
60
+ :ok
61
+ end
62
+ end
63
+ end
64
+
65
+ def current_verb_scope
66
+ if current_user && (scope_name = account_verb_scope?)
67
+ # log "got an account_verb_scope #{scope_name}."
68
+ mdl.send scope_name, current_user
69
+ elsif !(scope_name = public_verb_scope?)
70
+ log "No #{current_user.nil? ? 'public' : 'account'} #{scope_name_for_action} scope available for #{mdl}.#{' May be available after login.' if account_verb_scope?}"
71
+ nil
72
+ else
73
+ # log "got a #{scope_name} public_verb_scope."
74
+ mdl.send scope_name
75
+ end
76
+ end
77
+
78
+ def nesting_scope_list
79
+ @hammock_cached_nesting_scope_list = current_hammock_resource.parent.nesting_scope_list_for params.selekt {|k,v| /_id$/ =~ k }
80
+ end
81
+
82
+ def current_nest_scope
83
+ nesting_scope_list.reverse.inject {|acc,scope| acc.within scope }
84
+ end
85
+
86
+ def current_scope
87
+ log "#{current_hammock_resource.mdl}, #{current_hammock_resource.ancestry.map(&:mdl).inspect}"
88
+ if (verb_scope = current_verb_scope).nil?
89
+ nil
90
+ elsif (resultant_scope = verb_scope.within(current_nest_scope, current_hammock_resource.routing_parent)).nil?
91
+ nil
92
+ else
93
+ # puts "nest: #{current_nest_scope.clauses.inspect}"
94
+ # puts "verb: #{current_verb_scope.clauses.inspect}"
95
+ puts "chained in (#{resultant_scope.owner}) current_scope: #{resultant_scope.clauses.inspect}"
96
+ resultant_scope = resultant_scope.chain(custom_scope) unless custom_scope.nil?
97
+ resultant_scope.sort_by &mdl.sorter
98
+ end
99
+ end
100
+
101
+
102
+ private
103
+
104
+ def scope_name_for_action
105
+ if 'index' == action_name
106
+ 'index'
107
+ elsif safe_verb_and_implication?
108
+ 'read'
109
+ else
110
+ 'write'
111
+ end
112
+ end
113
+
114
+ def requester_name
115
+ current_user.nil? ? 'Anonymous' : "#{current_user.class}<#{current_user.id}>"
116
+ end
117
+
118
+ def account_verb_scope?
119
+ mdl.has_account_scope? scope_name_for_action
120
+ end
121
+ def public_verb_scope?
122
+ mdl.has_public_scope? scope_name_for_action
123
+ end
124
+
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,36 @@
1
+ module Hammock
2
+ module Suggest
3
+ MixInto = ActiveRecord::Base
4
+
5
+ def self.included base # :nodoc:
6
+ base.send :include, InstanceMethods
7
+ base.send :extend, ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ def suggest given_fields, queries, limit = 15
13
+ if (fields = given_fields & columns.map(&:name)).length != given_fields.length
14
+ log "Invalid columns #{(given_fields - fields).inspect}."
15
+ else
16
+ find(:all,
17
+ :limit => limit,
18
+ :order => fields.map {|f| "#{f} ASC" }.join(', '),
19
+ :conditions => [
20
+ fields.map {|f|
21
+ ([ "LOWER(#{table_name}.#{f}) LIKE ?" ] * queries.length).join(' AND ')
22
+ }.map {|clause|
23
+ "(#{clause})"
24
+ }.join(' OR ')
25
+ ].concat(queries.map{|q| "%#{q}%" } * fields.length)
26
+ )
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ module InstanceMethods
33
+
34
+ end
35
+ end
36
+ end