guise 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Eduardo Gutierrez
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # guise
2
+
3
+ An alternative to storing role resources in the database.
4
+
5
+ guise delegates type information to roles table that determines what a resource
6
+ can be instantiated as. In essence, it's similar to single table inheritance,
7
+ but with multiple inheritances possible.
8
+
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'guise'
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ ```
21
+ $ bundle
22
+ ```
23
+
24
+ Or install it yourself as:
25
+
26
+ ```
27
+ $ gem install guise
28
+ ```
29
+
30
+
31
+ ## Usage
32
+
33
+ Create a table to store your type information:
34
+
35
+ ```
36
+ rails generate model guises user:references title:string
37
+ rake db:migrate
38
+ ```
39
+
40
+ Then add the `guises` call to your model. If your table name is different or
41
+ you want a different attribute, supply the `:as` and `:attribute` options.
42
+
43
+ ```ruby
44
+ class User < ActiveRecord::Base
45
+ has_many :guises
46
+ has_guises :DeskWorker, :MailFowarder
47
+ end
48
+
49
+ class Person < ActiveRecord::Base
50
+ has_many :positions
51
+ has_guises :Admin, :Engineer, :association => :positions, :attribute => :rank
52
+ end
53
+ ```
54
+
55
+ This adds the following methods to the `User` class:
56
+ * `:desk_workers` and `:mail_forwarders` scopes
57
+ * `:has_role?` that checks if a user is a particular type
58
+ * `:desk_worker?`, `:mail_forwarder` that proxy to `:has_role?`
59
+ * `:has_roles?` that checks if a user is of any of the types supplied
60
+ * Aliases the association method (`:positions`) as `:guises` if association is not called `:guises`
61
+
62
+ Additionally, this creates classes `DeskWorker` and `MailForwarder` that:
63
+ * Inherit from `User`.
64
+ * Have default scopes for `:desk_workers` and `:mail_forwarders` respectively.
65
+ * Create users with the right associated occupation.
66
+
67
+
68
+ ## Plans
69
+
70
+ * Provide generators for roles table
71
+ * Update `has_guises` method to setup `has_many` association
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/guise.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/guise/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Eduardo Gutierrez"]
6
+ gem.email = ["edd_d@mit.edu"]
7
+ gem.description = %q{ Databse-indempotent roles (mostly) }
8
+ gem.summary = %q{ Guise provides a (hopefully) reasonable paradigm for user roles on top of ActiveRecord }
9
+ gem.homepage = "https://github.com/ecbypi/guise"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "guise"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Guise::VERSION
17
+
18
+ gem.add_dependency "activerecord", "~> 3.0"
19
+ gem.add_development_dependency "rspec", "~> 2.9"
20
+ gem.add_development_dependency "sqlite3", "~> 1.3.3"
21
+ gem.add_development_dependency "factory_girl", "~> 3.2"
22
+ end
@@ -0,0 +1,27 @@
1
+ module Guise
2
+ module Attributes
3
+ def set_attributes(names, options)
4
+ @@guise_attributes = options.reverse_merge(
5
+ :association => :guises,
6
+ :attribute => :title,
7
+ :names => names
8
+ )
9
+
10
+ class_eval do
11
+ alias_method :guises, guise_association
12
+ end if guise_association != :guises
13
+ end
14
+
15
+ def guises
16
+ @@guise_attributes[:names]
17
+ end
18
+
19
+ def guise_association
20
+ @@guise_attributes[:association]
21
+ end
22
+
23
+ def guise_attribute
24
+ @@guise_attributes[:attribute]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,35 @@
1
+ module Guise
2
+ module Inheritance
3
+ def build_guises
4
+ guises.each do |name|
5
+ scope_name = name.tableize.to_sym
6
+ introspective_name = "#{name.underscore}?"
7
+
8
+ # Add a scope for this type of resource
9
+ scope scope_name, joins(guise_association).where(guise_association => { guise_attribute => name })
10
+
11
+ # build the class setting it's default scope to limit to those of itself
12
+ guise_class = Class.new(self) do
13
+ default_scope { send(scope_name) }
14
+
15
+ after_initialize do
16
+ self.guises.new(self.guise_attribute => name) unless self.has_role?(name)
17
+ end
18
+
19
+ after_create do
20
+ self.guises.create(self.guise_attribute => name)
21
+ end
22
+ end
23
+
24
+ Object.const_set(name, guise_class)
25
+
26
+ # define the introspection method for the type
27
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
28
+ def #{introspective_name}
29
+ has_role?(#{name})
30
+ end
31
+ METHOD
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ module Guise
2
+ module Introspection
3
+
4
+ def has_role?(name)
5
+ name = name.to_s.classify
6
+
7
+ if !self.class.guises.include?(name)
8
+ raise NameError, "no such guise #{name}"
9
+ end
10
+
11
+ guises.pluck(guise_attribute).include?(name)
12
+ end
13
+
14
+ def has_any_roles?(*names)
15
+ names.map(&method(:has_role?)).any?
16
+ end
17
+
18
+ def has_roles?(*names)
19
+ names.map(&method(:has_role?)).all?
20
+ end
21
+
22
+ protected
23
+
24
+ def guise_attribute
25
+ self.class.guise_attribute
26
+ end
27
+
28
+ def guise_table
29
+ self.class.guise_table
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Guise
2
+ VERSION = "0.1.1"
3
+ end
data/lib/guise.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'guise/version'
2
+ require 'guise/attributes'
3
+ require 'guise/inheritance'
4
+ require 'guise/introspection'
5
+
6
+ module Guise
7
+ def self.extended(base)
8
+ base.extend Inheritance
9
+ base.extend Attributes
10
+ base.send :include, Introspection
11
+ end
12
+
13
+ def has_guises(*names)
14
+ options = names.last.is_a?(Hash) ? names.pop : {}
15
+ class_names = names.map(&:to_s).map(&:classify)
16
+
17
+ set_attributes(class_names, options)
18
+ build_guises
19
+ end
20
+ end
21
+
22
+ ActiveRecord::Base.extend Guise
data/spec/factories.rb ADDED
@@ -0,0 +1,24 @@
1
+ FactoryGirl.define do
2
+
3
+ factory :user do
4
+ name 'Micro Helpline'
5
+ email 'mrhalp@mit.edu'
6
+
7
+ factory :technician do
8
+ after_create do |user|
9
+ FactoryGirl.create(:user_role, :name => 'Technician', :user => user)
10
+ end
11
+ end
12
+
13
+ factory :supervisor do
14
+ after_create do |user|
15
+ FactoryGirl.create(:user_role, :name => 'Supervisor', :user => user)
16
+ end
17
+ end
18
+ end
19
+
20
+ factory :user_role do
21
+ association :user
22
+ name 'Technician'
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ module Guise
4
+ describe Inheritance do
5
+ let!(:user) { create(:user) }
6
+ let!(:technician) { create(:technician) }
7
+ let!(:supervisor) { create(:supervisor) }
8
+
9
+ it "builds subclasses of names called in :guise" do
10
+ Technician.new.should be_a User
11
+ Technician.new.guises.should_not be_empty
12
+ end
13
+
14
+ it "adds scopes for each type" do
15
+ User.technicians.should include(technician)
16
+ User.technicians.should_not include(user)
17
+
18
+ User.supervisors.should include(supervisor)
19
+ User.supervisors.should_not include(user)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ module Guise
4
+ describe Introspection do
5
+ let!(:user) { create(:user) }
6
+ let!(:technician) { create(:technician) }
7
+ let!(:supervisor) { create(:supervisor) }
8
+
9
+ describe "#has_role?" do
10
+ it "checks if resource is of the type provided" do
11
+ user.has_role?(:technician).should be_false
12
+ technician.has_role?(:Technician).should be_true
13
+ end
14
+
15
+ it "raises an error if type was not added in :guises call" do
16
+ expect { user.has_role?(:Accountant) }.to raise_error(NameError)
17
+ end
18
+ end
19
+
20
+ describe "#has_roles?" do
21
+ before :each do
22
+ create(:user_role, :name => 'Technician', :user => supervisor)
23
+ end
24
+
25
+ it "checks if resource is all of the provided types" do
26
+ technician.has_roles?(:Supervisor, :Technician).should be_false
27
+ supervisor.has_roles?('Supervisor', Technician).should be_true
28
+ end
29
+ end
30
+
31
+ describe "#has_any_roles?" do
32
+ it "checks if resource is any of the supplied roles" do
33
+ user.has_any_roles?(:Supervisor, :Technician).should be_false
34
+ technician.has_any_roles?('supervisor', 'technician').should be_true
35
+ end
36
+ end
37
+
38
+ it "adds methods that proxy to #has_role? for ease" do
39
+ user.should respond_to :technician?
40
+ user.should respond_to :supervisor?
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,14 @@
1
+ require 'factory_girl'
2
+
3
+ require File.expand_path('../support/database.rb', __FILE__)
4
+
5
+ RSpec.configure do |config|
6
+
7
+ config.before :suite do
8
+ Database.create
9
+ FactoryGirl.find_definitions
10
+ end
11
+
12
+ config.include FactoryGirl::Syntax::Methods
13
+ end
14
+
@@ -0,0 +1,39 @@
1
+ # Inspired/borrowed from @ernie's way of building out a database in
2
+ # ransack and squeel
3
+
4
+ require 'active_record'
5
+ require 'guise'
6
+
7
+ ActiveRecord::Base.establish_connection(
8
+ :adapter => 'sqlite3',
9
+ :database => ':memory:'
10
+ )
11
+
12
+ class User < ActiveRecord::Base
13
+ has_many :user_roles
14
+ has_guises :Technician, :Supervisor, :association => :user_roles, :attribute => :name
15
+ end
16
+
17
+ class UserRole < ActiveRecord::Base
18
+ belongs_to :user
19
+ end
20
+
21
+ module Database
22
+ def self.create
23
+ ActiveRecord::Base.silence do
24
+ ActiveRecord::Migration.verbose = false
25
+
26
+ ActiveRecord::Schema.define do
27
+ create_table :users, :force => true do |t|
28
+ t.string :name
29
+ t.string :email
30
+ end
31
+
32
+ create_table :user_roles, :force => true do |t|
33
+ t.string :name
34
+ t.integer :user_id
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: guise
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Eduardo Gutierrez
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: &70099999163140 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70099999163140
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &70099999161660 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '2.9'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70099999161660
36
+ - !ruby/object:Gem::Dependency
37
+ name: sqlite3
38
+ requirement: &70099999160760 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.3.3
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70099999160760
47
+ - !ruby/object:Gem::Dependency
48
+ name: factory_girl
49
+ requirement: &70099999159300 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '3.2'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70099999159300
58
+ description: ! ' Databse-indempotent roles (mostly) '
59
+ email:
60
+ - edd_d@mit.edu
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - .rspec
67
+ - Gemfile
68
+ - LICENSE
69
+ - README.md
70
+ - Rakefile
71
+ - guise.gemspec
72
+ - lib/guise.rb
73
+ - lib/guise/attributes.rb
74
+ - lib/guise/inheritance.rb
75
+ - lib/guise/introspection.rb
76
+ - lib/guise/version.rb
77
+ - spec/factories.rb
78
+ - spec/guise/inheritance_spec.rb
79
+ - spec/guise/introspection_spec.rb
80
+ - spec/spec_helper.rb
81
+ - spec/support/database.rb
82
+ homepage: https://github.com/ecbypi/guise
83
+ licenses: []
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 1.8.11
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Guise provides a (hopefully) reasonable paradigm for user roles on top of
106
+ ActiveRecord
107
+ test_files:
108
+ - spec/factories.rb
109
+ - spec/guise/inheritance_spec.rb
110
+ - spec/guise/introspection_spec.rb
111
+ - spec/spec_helper.rb
112
+ - spec/support/database.rb