hammock 0.3.3 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,22 +1,42 @@
1
+ == 0.3.5 2009-06-03
2
+ Converted README back to rdoc, for rubyforge rake tasks.
3
+
4
+
5
+ == 0.3.4 2009-06-03
6
+ Converted lambda and proc to L.
7
+ Include exception message on error in Hammock.load_models.
8
+ Fixed lambda -> L alias, by adding initialize_for_load on hammock init.
9
+ Added Hammock::Mutex, containing AR::Base#mutex, to run a closure within a column-based mutex.
10
+ Added Hammock::Aliases, initially with lambda aliased as L.
11
+ Removed model-loading logging.
12
+ README formatting updates, converted to Markdown.
13
+ Updated README to match current code state.
14
+ Updated the bit about the screencast in the README.
15
+
16
+
1
17
  == 0.3.3 2009-05-28
2
18
  On suggest, :limit => 0 means no limit now, as it should.
3
19
 
4
20
 
5
21
  == 0.3.2 2009-05-28
6
- Accidentally pushed 0.3.1 in an unknown state - bumped to 0.3.2 to be safe.
7
-
8
-
9
- == 0.3.1 2009-05-28
10
22
  Added Hammock.load_models, along with call in draw_with_hammock_route_map_init.
11
23
  Refactored Hammock.included out into load_hammock_components and related methods, and made them all private.
12
24
  Only extract record_attributes from records - not resources too.
13
25
  Fixed http status entry in log_hit.
14
26
 
15
27
 
28
+ == 0.3.1 2009-05-26
29
+ Check for a specific scope for the current action before checking for a default-named scope.
30
+ Commented ambition query logging.
31
+ Moved index rendering to restful_rendering.
32
+ Wrap pending -> success notification within obj null test.
33
+
34
+
16
35
  == 0.3.0 2009-05-25
17
36
  Updated ajax_link, ajaxinate and related methods to accept explicit args instead of a splatted list - this makes it a lot easier to handle different amounts of entities.
18
37
  Only do obj replacement in jquery_xhr callback if it's set.
19
38
 
39
+
20
40
  == 0.2.25 2009-05-25
21
41
  Added String#clean_email[!].
22
42
  Moved set_new_or_deleted_before_save callback from controller to model.
data/README.rdoc CHANGED
@@ -14,22 +14,34 @@ Hammock tackles the hard and soft sides of security at once with a scoping secur
14
14
 
15
15
  Hammock inspects your routes and resources to generate a routing tree for each resource. Parent resources in a nested route are handled transparently at every point - record retrieval, creation, and linking.
16
16
 
17
- It makes more sense when you see how it works though, so check out the screencast!
17
+ It makes more sense when you see how it works though. There's a screencast coming soon.
18
18
 
19
19
 
20
20
  == REQUIREMENTS:
21
21
 
22
- benhoskings-ambition
23
- benhoskings-ambitious-activerecord
22
+ benhoskings-ambition
23
+ benhoskings-ambitious-activerecord
24
+
25
+ These gems will install automatically as long as you've added the GitHub gem source:
26
+
27
+ gem sources -a http://gems.github.com
24
28
 
25
29
 
26
30
  == INSTALL:
27
31
 
28
- sudo gem install benhoskings-hammock --source http://gems.github.com
32
+ sudo gem install hammock
33
+
34
+ in config/environment.rb:
35
+
36
+ Rails::Initializer.run do |config|
37
+ config.gem 'hammock'
38
+ ...
29
39
 
30
- class ApplicationController
31
- include Hammock
32
- ...
40
+ in app/controllers/application_controller.rb:
41
+
42
+ class ApplicationController
43
+ include Hammock::RestfulActions
44
+ ...
33
45
 
34
46
 
35
47
  == LICENSE:
@@ -41,59 +53,67 @@ Hammock is licensed under the BSD license, which can be found in full in the LIC
41
53
 
42
54
  At the moment, you can do this with Hammock:
43
55
 
44
- class ApplicationController < ActionController::Base
45
- include Hammock
46
- end
56
+ class ApplicationController < ActionController::Base
57
+ include Hammock::RestfulActions
58
+ end
47
59
 
48
- class BeersController < ApplicationController
49
- end
60
+ class BeersController < ApplicationController
61
+ end
50
62
 
51
- class Person < ActiveRecord::Base
52
- end
63
+ class Person < ActiveRecord::Base
64
+ end
53
65
 
54
- class Beer < ActiveRecord::Base
55
- belongs_to :creator, :class_name => 'Person'
56
- belongs_to :recipient, :class_name => 'Person'
66
+ class Beer < ActiveRecord::Base
67
+ belongs_to :creator, :class_name => 'Person'
68
+ belongs_to :recipient, :class_name => 'Person'
57
69
 
58
- def read_scope_for account
59
- proc {|beer| beer.creator_id == account.id || beer.recipient_id == account.id }
60
- end
61
- export_scope :read
62
- export_scope :read, :as => :index
63
-
64
- def write_scope_for account
65
- proc {|beer| record.creator_id == account.id }
70
+ def self.read_scope_for account
71
+ L{|beer| beer.creator_id == account.id || beer.recipient_id == account.id }
72
+ end
73
+ export_scope :read
74
+
75
+ # TODO - Duplication, yuck. There's a proper DSL in the pipes.
76
+ def self.index_scope_for account
77
+ L{|beer| beer.creator_id == account.id || beer.recipient_id == account.id }
78
+ end
79
+ export_scope :index
80
+
81
+ creator_scope_for :write
66
82
  end
67
- export_scope :write
68
- end
69
83
 
70
- <% @beers.each do |beer| %>
71
- From <%= beer.creator.name %> to <%= beer.recipient.name %>, <%= beer.reason %>, rated <%= beer.rating %>
72
- <%= hamlink_to :edit, beer %>
73
- <% end %>
84
+ <% @beers.each do |beer| %>
85
+ From <%= beer.creator.name %> to <%= beer.recipient.name %>, <%= beer.reason %>, rated <%= beer.rating %>
86
+ <%= hamlink_to :edit, beer %>
87
+ <% end %>
74
88
 
75
- The scope methods above require just one thing -- a context-free proc object that takes an ActiveRecord record as its argument, and returns true iff that record is within the scope for the specified account. Hammock uses the method (e.g. Beer.read_scope_for) to define resource and record scopes for the model:
89
+ The scope methods above require just one thing -- a context-free lambda that takes an ActiveRecord record as its argument, and returns true iff that record is within the scope for the specified account. Hammock uses the method (e.g. Beer.read_scope_for) to define resource and record scopes for the model:
76
90
 
77
- Beer.readable_by(account): the set of Beer records whose existence can be known by account
78
- Beer#readable_by?(account): returns true if the existence of this Beer instance can be known by account
91
+ Beer.readable_by(account): the set of Beer records whose existence can be known by account
92
+ Beer#readable_by?(account): returns true if the existence of this Beer instance can be known by account
79
93
 
80
94
  You define the logic for read, index and write scopes in Beer.[read,index,write]_scope_for, and the rest just works.
81
95
 
82
96
  These scope definitions are exploited extensively, to provide index selection, scoping for record selection, and post-selection object checks.
83
97
 
84
- - They provide the conditions that should be applied to retrieve the index of each resource. The scope is used transperently by Hammock on /beers -> BeersController#index, and is available for use through Beer.indexable_by(account).
98
+ - They provide the conditions that should be applied to retrieve the index of each resource.
85
99
 
86
- - They provide a scope within which records are searched for on single-record actions. For example, given the request /beers/5 -> BeersController#show{:id => 5}, Rails would generate the following SQL:
100
+ The scope is used transperently by Hammock on /beers -> BeersController#index, and is available for use through Beer.indexable_by(account).
87
101
 
88
- SELECT * FROM "beers" WHERE (beers."id" = 5) LIMIT 1
102
+ - They provide a scope within which records are searched for on single-record actions.
103
+
104
+ For example, given the request /beers/5 -> BeersController#show{:id => 5}, Rails would generate the following SQL:
105
+
106
+ SELECT * FROM "beers" WHERE (beers."id" = 5) LIMIT 1
89
107
 
90
108
  Hammock uses the conditions specified in Beer.read_scope_for to generate (assuming an account_id of 3):
91
109
 
92
- SELECT * FROM "beers" WHERE ((beers.creator_id = 3 OR beers.recipient_id = 3) AND beers."id" = 5) LIMIT 1
110
+ SELECT * FROM "beers" WHERE ((beers.creator_id = 3 OR beers.recipient_id = 3) AND beers."id" = 5) LIMIT 1
93
111
 
94
112
  Hammock uses Beer.read_scope_for on #show, and write_scope_for on #edit, #update and #destroy. These scopes can be accessed as above through Beer.readable_by(account) and Beer.writeable_by(account). This eliminates authorization checks from the action, because if the ID of a Beer is provided that the user doesn't have access to it will fall outside the scope and will not be found in the DB at all.
95
113
 
96
- - They are used to discover credentials for already-queried ActiveRecord objects, without touching the database again. Just as Beer.readable_by(account) returns the set of Beer records whose existence can be known by account, @beer.readable_by?(account) returns true iff @beer's existence can be known by account. This is employed by hamlink_to.
114
+ - They are used to discover credentials for already-queried ActiveRecord objects, without touching the database again.
115
+
116
+ Just as Beer.readable_by(account) returns the set of Beer records whose existence can be known by account, @beer.readable_by?(account) returns true iff @beer's existence can be known by account. This is employed by hamlink_to.
97
117
 
98
118
  These three uses of the scope, plus another as-yet unimplemented bit, provide the entire security model of the application.
99
119
 
data/hammock.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{hammock}
5
- s.version = "0.3.3"
5
+ s.version = "0.3.5"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Ben Hoskings"]
9
- s.date = %q{2009-05-28}
9
+ s.date = %q{2009-06-03}
10
10
  s.description = %q{Hammock is a Rails plugin that eliminates redundant code in a very RESTful manner. It does this in lots in lots of different places, but in one manner: it encourages specification in place of implementation.
11
11
 
12
12
 
@@ -16,7 +16,7 @@ Hammock tackles the hard and soft sides of security at once with a scoping secur
16
16
 
17
17
  Hammock inspects your routes and resources to generate a routing tree for each resource. Parent resources in a nested route are handled transparently at every point - record retrieval, creation, and linking.
18
18
 
19
- It makes more sense when you see how it works though, so check out the screencast!}
19
+ It makes more sense when you see how it works though. There's a screencast coming soon.}
20
20
  s.email = ["ben@hoskings.net"]
21
21
  s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.rdoc", "misc/scaffold.txt"]
22
22
  s.files = ["History.txt", "LICENSE", "Manifest.txt", "README.rdoc", "Rakefile", "hammock.gemspec", "lib/hammock.rb", "lib/hammock/ajaxinate.rb", "lib/hammock/callback.rb", "lib/hammock/callbacks.rb", "lib/hammock/canned_scopes.rb", "lib/hammock/constants.rb", "lib/hammock/controller_attributes.rb", "lib/hammock/export_scope.rb", "lib/hammock/hamlink_to.rb", "lib/hammock/javascript_buffer.rb", "lib/hammock/logging.rb", "lib/hammock/model_attributes.rb", "lib/hammock/model_logging.rb", "lib/hammock/monkey_patches/action_pack.rb", "lib/hammock/monkey_patches/active_record.rb", "lib/hammock/monkey_patches/array.rb", "lib/hammock/monkey_patches/hash.rb", "lib/hammock/monkey_patches/logger.rb", "lib/hammock/monkey_patches/module.rb", "lib/hammock/monkey_patches/numeric.rb", "lib/hammock/monkey_patches/object.rb", "lib/hammock/monkey_patches/route_set.rb", "lib/hammock/monkey_patches/string.rb", "lib/hammock/overrides.rb", "lib/hammock/resource_mapping_hooks.rb", "lib/hammock/resource_retrieval.rb", "lib/hammock/restful_actions.rb", "lib/hammock/restful_rendering.rb", "lib/hammock/restful_support.rb", "lib/hammock/route_drawing_hooks.rb", "lib/hammock/route_for.rb", "lib/hammock/route_node.rb", "lib/hammock/route_step.rb", "lib/hammock/scope.rb", "lib/hammock/suggest.rb", "lib/hammock/utils.rb", "misc/scaffold.txt", "misc/template.rb", "tasks/hammock_tasks.rake", "test/hammock_test.rb"]
@@ -79,8 +79,10 @@ module Hammock
79
79
  ),
80
80
  function(response, textStatus) {
81
81
  #{response_action}
82
- if (obj) obj.children('.spinner').hide();
83
- #{"if ('success' == textStatus) obj.children('.success').show().fadeOut(4000);" if opts[:spinner] != :pending}
82
+ if (obj) {
83
+ obj.children('.spinner').hide();
84
+ #{"if ('success' == textStatus) obj.children('.success').show().fadeOut(4000);" if opts[:spinner] != :pending}
85
+ }
84
86
  eval("#{clean_snippet opts[:after]}");
85
87
  }
86
88
  );
@@ -89,24 +89,24 @@ module Hammock
89
89
 
90
90
  def public_scope account = nil
91
91
  if sqlite?
92
- lambda {|record| 1 }
92
+ L{|record| 1 }
93
93
  else
94
- lambda {|record| true }
94
+ L{|record| true }
95
95
  end
96
96
  end
97
97
 
98
98
  def authed_scope account
99
99
  has_account = !account.nil?
100
- lambda {|record| has_account }
100
+ L{|record| has_account }
101
101
  end
102
102
 
103
103
  def creator_scope account
104
- lambda {|record| record.creator_id == account.id }
104
+ L{|record| record.creator_id == account.id }
105
105
  end
106
106
 
107
107
  def partitioned_scope account
108
108
  if account.is_a? self
109
- lambda {|record| record.id == account.id }
109
+ L{|record| record.id == account.id }
110
110
  else
111
111
  empty_scope
112
112
  end
@@ -114,9 +114,9 @@ module Hammock
114
114
 
115
115
  def empty_scope account = nil
116
116
  if sqlite?
117
- lambda {|record| 0 } # TODO check what this should be
117
+ L{|record| 0 } # TODO check what this should be
118
118
  else
119
- lambda {|record| false }
119
+ L{|record| false }
120
120
  end
121
121
  end
122
122
 
@@ -35,7 +35,7 @@ module Hammock
35
35
 
36
36
  def sorter
37
37
  # TODO updated_at DESC
38
- proc {|record| record.id }
38
+ L{|record| record.id }
39
39
  end
40
40
 
41
41
  def resource
@@ -6,14 +6,7 @@ module Hammock
6
6
  #
7
7
  # Lists the current resource's records that are visible within the current index scope, defined by +index_scope+ and +index_scope_for+ on the current model.
8
8
  def index
9
- if tasks_for_index
10
- respond_to do |format|
11
- format.html
12
- format.xml { render :xml => @records.kick }
13
- format.json { render :json => @records.kick }
14
- format.yaml { render :text => @records.kick.to_yaml }
15
- end
16
- end
9
+ render_for_index if tasks_for_index
17
10
  end
18
11
 
19
12
  # The +new+ action. (GET, safe, idempotent)
@@ -28,6 +28,15 @@ module Hammock
28
28
  end
29
29
  end
30
30
 
31
+ def render_for_index
32
+ respond_to do |format|
33
+ format.html
34
+ format.xml { render :xml => @records.kick }
35
+ format.json { render :json => @records.kick }
36
+ format.yaml { render :text => @records.kick.to_yaml }
37
+ end
38
+ end
39
+
31
40
  def render_for_safe_actions result = true, opts = {}
32
41
  if request.xhr?
33
42
  if params[:attribute]
@@ -77,7 +77,7 @@ module Hammock
77
77
  elsif record_or_records.is_a? ActiveRecord::Base
78
78
  instance_variable_set "@#{mdl_name}", (@record = record_or_records)
79
79
  elsif record_or_records.is_a? Ambition::Context
80
- log "Unkicked query: #{record_or_records.to_hash.inspect}"
80
+ # log "Unkicked query: #{record_or_records.to_hash.inspect}"
81
81
  instance_variable_set "@#{mdl_name.pluralize}", (@records = record_or_records)
82
82
  elsif record_or_records.is_a? Array
83
83
  instance_variable_set "@#{mdl_name.pluralize}", (@records = record_or_records)
data/lib/hammock/scope.rb CHANGED
@@ -116,10 +116,10 @@ module Hammock
116
116
  end
117
117
 
118
118
  def account_verb_scope?
119
- mdl.has_account_scope? scope_name_for_action
119
+ mdl.has_account_scope?(action_name) || mdl.has_account_scope?(scope_name_for_action)
120
120
  end
121
121
  def public_verb_scope?
122
- mdl.has_public_scope? scope_name_for_action
122
+ mdl.has_public_scope?(action_name) || mdl.has_public_scope?(scope_name_for_action)
123
123
  end
124
124
 
125
125
  end
data/lib/hammock.rb CHANGED
@@ -4,10 +4,11 @@ require 'ambition'
4
4
  require 'ambition/adapters/active_record'
5
5
 
6
6
  module Hammock
7
- VERSION = '0.3.3'
7
+ VERSION = '0.3.5'
8
8
 
9
9
  def self.included base # :nodoc:
10
10
  puts "Loading Hammock from #{loaded_from_gem? ? 'gem' : 'plugin'}"
11
+ initialize_for_load
11
12
  load_hammock_components base
12
13
  end
13
14
 
@@ -23,9 +24,8 @@ module Hammock
23
24
  klass = File.basename(model_file, '.rb').classify
24
25
  begin
25
26
  Object.const_get klass
26
- puts "Loaded #{klass} from #{model_file}."
27
27
  rescue
28
- puts "Couldn't load #{klass} from #{model_file}."
28
+ puts "Couldn't load #{klass} from #{model_file}: #{$!}."
29
29
  end
30
30
  }
31
31
  end
@@ -33,6 +33,10 @@ module Hammock
33
33
 
34
34
  private
35
35
 
36
+ def self.initialize_for_load
37
+ Hammock::Aliases.alias_lambda
38
+ end
39
+
36
40
  def self.load_hammock_components base
37
41
  require_hammock_components
38
42
  mixin_hammock_components base
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hammock
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Hoskings
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-28 00:00:00 +10:00
12
+ date: 2009-06-03 00:00:00 +10:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -62,7 +62,7 @@ description: |-
62
62
 
63
63
  Hammock inspects your routes and resources to generate a routing tree for each resource. Parent resources in a nested route are handled transparently at every point - record retrieval, creation, and linking.
64
64
 
65
- It makes more sense when you see how it works though, so check out the screencast!
65
+ It makes more sense when you see how it works though. There's a screencast coming soon.
66
66
  email:
67
67
  - ben@hoskings.net
68
68
  executables: []