modularity 0.6.0 → 3.0.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 429e10c8fecef4d2111b5ca35805fe7d034869422e3ab1c098378e6f970f8caa
4
+ data.tar.gz: e0cecc3bda150b73fb39b7fff2eef48d328dba303bb8842b5aa28be09287f26f
5
+ SHA512:
6
+ metadata.gz: 4526b4df0b655e0eb5de00b82a65f8081bd51390bbb71e2ac3ff38c670b8b541e9b24df7935d563e51d7f07d8a6a3fc8af83c0e803321d5fef47238cd640f080
7
+ data.tar.gz: bd2ba2ab6fe3ba0150d07c43022d8eccd9f7bf17f669d1d4c6f6df129b5bdc42e22d9212c56886dff34c5f2d41217360fcbd3fb91ebd02e9984162b8d2aa93c7
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: Tests
3
+ 'on':
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+ branches:
9
+ - master
10
+ jobs:
11
+ test:
12
+ runs-on: ubuntu-20.04
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ include:
17
+ - ruby: 2.5.7
18
+ gemfile: Gemfile
19
+ - ruby: 2.7.4
20
+ gemfile: Gemfile
21
+ - ruby: 3.0.2
22
+ gemfile: Gemfile
23
+ env:
24
+ BUNDLE_GEMFILE: "${{ matrix.gemfile }}"
25
+ steps:
26
+ - uses: actions/checkout@v2
27
+ - name: Install ruby
28
+ uses: ruby/setup-ruby@v1
29
+ with:
30
+ ruby-version: "${{ matrix.ruby }}"
31
+ - name: Bundle
32
+ run: |
33
+ gem install bundler:2.2.22
34
+ bundle install --no-deployment
35
+ - name: Run tests
36
+ run: bundle exec rspec
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  doc
2
2
  pkg
3
3
  *.gem
4
+ .idea
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.5.7
data/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
5
+
6
+ ## Unreleased
7
+
8
+ ### Breaking changes
9
+
10
+ ### Compatible changes
11
+
12
+
13
+ ## 3.0.0 - 2021-08-24
14
+
15
+ ### Breaking changes
16
+
17
+ - Removed migration guide for modularity version 1.
18
+ - Removed support for Ruby < `2.5.0`.
19
+
20
+ ### Compatible changes
21
+
22
+ - Added this CHANGELOG file.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'pry-byebug'
8
+ gem 'gemika'
data/Gemfile.lock ADDED
@@ -0,0 +1,47 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ modularity (3.0.0)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ byebug (11.1.3)
10
+ coderay (1.1.3)
11
+ diff-lcs (1.4.4)
12
+ gemika (0.6.1)
13
+ method_source (1.0.0)
14
+ pry (0.13.1)
15
+ coderay (~> 1.1)
16
+ method_source (~> 1.0)
17
+ pry-byebug (3.9.0)
18
+ byebug (~> 11.0)
19
+ pry (~> 0.13.0)
20
+ rake (13.0.6)
21
+ rspec (3.10.0)
22
+ rspec-core (~> 3.10.0)
23
+ rspec-expectations (~> 3.10.0)
24
+ rspec-mocks (~> 3.10.0)
25
+ rspec-core (3.10.1)
26
+ rspec-support (~> 3.10.0)
27
+ rspec-expectations (3.10.1)
28
+ diff-lcs (>= 1.2.0, < 2.0)
29
+ rspec-support (~> 3.10.0)
30
+ rspec-mocks (3.10.2)
31
+ diff-lcs (>= 1.2.0, < 2.0)
32
+ rspec-support (~> 3.10.0)
33
+ rspec-support (3.10.2)
34
+
35
+ PLATFORMS
36
+ ruby
37
+ x86_64-linux
38
+
39
+ DEPENDENCIES
40
+ gemika
41
+ modularity!
42
+ pry-byebug
43
+ rake
44
+ rspec
45
+
46
+ BUNDLED WITH
47
+ 2.2.26
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2009 Henning Koch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,128 @@
1
+ [![Tests](https://github.com/makandra/modularity/workflows/Tests/badge.svg)](https://github.com/makandra/modularity/actions)
2
+
3
+ # Modularity 2 - Traits and partial classes for Ruby
4
+
5
+ Modularity enhances Ruby's [`Module`](http://apidock.com/ruby/Module) so it can be used traits and partial classes.
6
+ This allows very simple definition of meta-programming macros like the
7
+ `has_many` that you know from Rails.
8
+
9
+ Modularity also lets you organize large models into multiple source files
10
+ in a way that is less awkward than using modules.
11
+
12
+ ## Installation
13
+
14
+ Add the following to your `Gemfile`:
15
+
16
+ ```
17
+ gem 'modularity'
18
+ ```
19
+
20
+ Now run `bundle install`.
21
+
22
+ ## Example 1: Easy meta-programming macros
23
+
24
+ Ruby allows you to construct classes using meta-programming macros like
25
+ `acts_as_tree` or `has_many :items`. These macros will add methods,
26
+ callbacks, etc. to the calling class. However, right now Ruby (and Rails) makes it awkward to define
27
+ such macros in your project as part of your application domain.
28
+
29
+ Modularity allows you to extract common behaviour into reusable macros by defining traits with parameters.
30
+ Your macros can live in your application, allowing you to express your application domain in both classes
31
+ and macros.
32
+
33
+ Here is an example of a `strip_field` macro, which created setter methods that remove leading and trailing whitespace from newly assigned values:
34
+
35
+ ```
36
+ # app/models/article.rb
37
+ class Article < ActiveRecord::Base
38
+ include DoesStripFields[:name, :brand]
39
+ end
40
+
41
+ # app/models/shared/does_strip_fields.rb
42
+ module DoesStripFields
43
+ as_trait do |*fields|
44
+ fields.each do |field|
45
+ define_method("#{field}=") do |value|
46
+ self[field] = value.strip
47
+ end
48
+ end
49
+ end
50
+ end
51
+ ```
52
+
53
+ Notice the `as_trait` block.
54
+
55
+ We like to add `app/models/shared` and `app/controllers/shared` to the load paths of our Rails projects.
56
+ These are great places to store macros that are re-used from multiple classes.
57
+
58
+ ## Example 2: Mixins with class methods
59
+
60
+ Using a module to add both instance methods and class methods is
61
+ [very awkward](http://redcorundum.blogspot.com/2006/06/mixing-in-class-methods.html).
62
+ Modularity does away with the clutter and lets you say this:
63
+
64
+ ```
65
+ # app/models/model.rb
66
+ class Model
67
+ include Mixin
68
+ end
69
+
70
+ # app/models/mixin.rb
71
+ module Mixin
72
+ as_trait do
73
+ def instance_method
74
+ # ...
75
+ end
76
+ def self.class_method
77
+ # ..
78
+ end
79
+ end
80
+ end
81
+ ```
82
+
83
+ `private` and `protected` will also work as expected when defining a trait.
84
+
85
+ ## Example 3: Splitting a model into multiple source files
86
+
87
+ Models are often concerned with multiple themes like "authentication", "contact info" or "permissions", each requiring
88
+ a couple of validations and callbacks here, and some method there. Modularity lets you organize your model into multiple
89
+ partial classes, so each file can deal with a single aspect of your model:
90
+
91
+ ```
92
+ # app/models/user.rb
93
+ class User < ActiveRecord::Base
94
+ include DoesAuthentication
95
+ include DoesPermissions
96
+ end
97
+
98
+ # app/models/user/does_authentication.rb
99
+ module User::DoesAuthentication
100
+ as_trait do
101
+ # methods, validations, etc. regarding usernames and passwords go here
102
+ end
103
+ end
104
+
105
+ # app/models/user/does_permissions.rb
106
+ module User::DoesPermissions
107
+ as_trait do
108
+ # methods, validations, etc. regarding contact information go here
109
+ end
110
+ end
111
+ ```
112
+
113
+ Some criticism has been raised for splitting large models into files like this.
114
+ Essentially, even though have an easier time navigating your code, you will still
115
+ have one giant model with many side effects.
116
+
117
+ There are [many better ways](http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/)
118
+ to decompose a huge Ruby class.
119
+
120
+ ## Development
121
+
122
+ * Install Bundler 2 `gem install bundler:2.2.22` and run `bundle install` to have a working development setup.
123
+ * Running tests for the current Ruby version: `bundle exec rake`
124
+ * Running tests for all supported Ruby version: Push the changes to Github in a feature branch, open a merge request and have a look at the test matrix in Github actions
125
+
126
+ ## Credits
127
+
128
+ Henning Koch from [makandra.com](http://makandra.com/)
data/Rakefile CHANGED
@@ -1,37 +1,5 @@
1
- require 'rake'
2
- # require 'rake/testtask'
3
- require 'rake/rdoctask'
4
- require 'spec/rake/spectask'
5
-
6
- desc 'Default: Run all specs.'
7
- task :default => :spec
8
-
9
- desc "Run all specs"
10
- Spec::Rake::SpecTask.new() do |t|
11
- t.spec_opts = ['--options', "\"spec/spec.opts\""]
12
- t.spec_files = FileList['spec/**/*_spec.rb']
13
- end
14
-
15
- desc 'Generate documentation for the modularity gem'
16
- Rake::RDocTask.new(:rdoc) do |rdoc|
17
- rdoc.rdoc_dir = 'rdoc'
18
- rdoc.title = 'modularity'
19
- rdoc.options << '--line-numbers' << '--inline-source'
20
- rdoc.rdoc_files.include('README')
21
- rdoc.rdoc_files.include('lib/**/*.rb')
22
- end
23
-
24
- begin
25
- require 'jeweler'
26
- Jeweler::Tasks.new do |gemspec|
27
- gemspec.name = "modularity"
28
- gemspec.summary = "Traits and partial classes for Ruby"
29
- gemspec.email = "github@makandra.de"
30
- gemspec.homepage = "http://github.com/makandra/modularity"
31
- gemspec.description = "Traits and partial classes for Ruby"
32
- gemspec.authors = ["Henning Koch"]
33
- end
34
- rescue LoadError
35
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
36
- end
1
+ require 'bundler/gem_tasks'
2
+ require 'bundler/setup'
3
+ require 'gemika/tasks'
37
4
 
5
+ task default: 'matrix:spec'
data/bin/console ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'modularity'
5
+ require 'pry'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+ Pry.start
@@ -1,9 +1,40 @@
1
1
  module Modularity
2
+
3
+ class ParametrizedTrait < Module
4
+
5
+ def initialize(blank_trait, args)
6
+ @args = args
7
+ @macro = blank_trait.instance_variable_get(:@modularity_macro)
8
+ include(blank_trait)
9
+ end
10
+
11
+ def included(base)
12
+ base.class_exec(*@args, &@macro)
13
+ end
14
+
15
+ end
16
+
2
17
  module AsTrait
3
- def as_trait(&block)
4
- @trait_macro = block
18
+
19
+ def as_trait(&macro)
20
+
21
+ @modularity_macro = macro
22
+
23
+ def self.included(base)
24
+ unless base.is_a?(ParametrizedTrait)
25
+ base.class_exec(&@modularity_macro)
26
+ end
27
+
28
+ end
29
+
30
+ def self.[](*args)
31
+ blank_trait = self
32
+ ParametrizedTrait.new(blank_trait, args)
33
+ end
34
+
5
35
  end
36
+
6
37
  end
7
38
  end
8
39
 
9
- Object.send :include, Modularity::AsTrait
40
+ Module.send(:include, Modularity::AsTrait)
@@ -0,0 +1,3 @@
1
+ module Modularity
2
+ VERSION = '3.0.0'
3
+ end
data/lib/modularity.rb CHANGED
@@ -1,2 +1 @@
1
- require 'modularity/does'
2
-
1
+ require 'modularity/as_trait'
data/modularity.gemspec CHANGED
@@ -1,56 +1,27 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
- # -*- encoding: utf-8 -*-
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'modularity/version'
5
4
 
6
- Gem::Specification.new do |s|
7
- s.name = %q{modularity}
8
- s.version = "0.6.0"
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'modularity'
7
+ spec.version = Modularity::VERSION
8
+ spec.required_ruby_version = '>= 2.5.0'
9
+ spec.authors = ['Henning Koch']
10
+ spec.email = ['henning.koch@makandra.de']
9
11
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Henning Koch"]
12
- s.date = %q{2010-02-18}
13
- s.description = %q{Traits and partial classes for Ruby}
14
- s.email = %q{github@makandra.de}
15
- s.extra_rdoc_files = [
16
- "README.rdoc"
17
- ]
18
- s.files = [
19
- ".gitignore",
20
- "MIT-LICENSE",
21
- "README.rdoc",
22
- "Rakefile",
23
- "VERSION",
24
- "lib/modularity.rb",
25
- "lib/modularity/as_trait.rb",
26
- "lib/modularity/does.rb",
27
- "lib/modularity/inflector.rb",
28
- "modularity.gemspec",
29
- "spec/as_trait_spec.rb",
30
- "spec/does_spec.rb",
31
- "spec/rcov.opts",
32
- "spec/spec.opts",
33
- "spec/spec_helper.rb"
34
- ]
35
- s.homepage = %q{http://github.com/makandra/modularity}
36
- s.rdoc_options = ["--charset=UTF-8"]
37
- s.require_paths = ["lib"]
38
- s.rubygems_version = %q{1.3.5}
39
- s.summary = %q{Traits and partial classes for Ruby}
40
- s.test_files = [
41
- "spec/spec_helper.rb",
42
- "spec/does_spec.rb",
43
- "spec/as_trait_spec.rb"
44
- ]
12
+ spec.summary = 'Traits and partial classes for Ruby'
13
+ spec.description = 'Traits and partial classes for Ruby'
14
+ spec.homepage = 'https://github.com/makandra/modularity'
15
+ spec.license = 'MIT'
45
16
 
46
- if s.respond_to? :specification_version then
47
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
- s.specification_version = 3
49
-
50
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
51
- else
52
- end
53
- else
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
54
21
  end
55
- end
22
+ spec.bindir = 'exe'
23
+ spec.executables = spec.files.grep(%r(^exe/)) { |f| File.basename(f) }
24
+ spec.require_paths = ['lib']
56
25
 
26
+ # Development dependencies are defined in the Gemfile (therefore no `spec.add_development_dependency` directives)
27
+ end
metadata CHANGED
@@ -1,71 +1,58 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: modularity
3
- version: !ruby/object:Gem::Version
4
- version: 0.6.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.0.0
5
5
  platform: ruby
6
- authors:
6
+ authors:
7
7
  - Henning Koch
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
-
12
- date: 2010-02-18 00:00:00 +01:00
13
- default_executable:
11
+ date: 2021-08-24 00:00:00.000000000 Z
14
12
  dependencies: []
15
-
16
13
  description: Traits and partial classes for Ruby
17
- email: github@makandra.de
14
+ email:
15
+ - henning.koch@makandra.de
18
16
  executables: []
19
-
20
17
  extensions: []
21
-
22
- extra_rdoc_files:
23
- - README.rdoc
24
- files:
25
- - .gitignore
26
- - MIT-LICENSE
27
- - README.rdoc
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".github/workflows/test.yml"
21
+ - ".gitignore"
22
+ - ".rspec"
23
+ - ".ruby-version"
24
+ - CHANGELOG.md
25
+ - Gemfile
26
+ - Gemfile.lock
27
+ - LICENSE.txt
28
+ - README.md
28
29
  - Rakefile
29
- - VERSION
30
+ - bin/console
30
31
  - lib/modularity.rb
31
32
  - lib/modularity/as_trait.rb
32
- - lib/modularity/does.rb
33
- - lib/modularity/inflector.rb
33
+ - lib/modularity/version.rb
34
34
  - modularity.gemspec
35
- - spec/as_trait_spec.rb
36
- - spec/does_spec.rb
37
- - spec/rcov.opts
38
- - spec/spec.opts
39
- - spec/spec_helper.rb
40
- has_rdoc: true
41
- homepage: http://github.com/makandra/modularity
42
- licenses: []
43
-
35
+ homepage: https://github.com/makandra/modularity
36
+ licenses:
37
+ - MIT
38
+ metadata: {}
44
39
  post_install_message:
45
- rdoc_options:
46
- - --charset=UTF-8
47
- require_paths:
40
+ rdoc_options: []
41
+ require_paths:
48
42
  - lib
49
- required_ruby_version: !ruby/object:Gem::Requirement
50
- requirements:
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
51
45
  - - ">="
52
- - !ruby/object:Gem::Version
53
- version: "0"
54
- version:
55
- required_rubygems_version: !ruby/object:Gem::Requirement
56
- requirements:
46
+ - !ruby/object:Gem::Version
47
+ version: 2.5.0
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
57
50
  - - ">="
58
- - !ruby/object:Gem::Version
59
- version: "0"
60
- version:
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
61
53
  requirements: []
62
-
63
- rubyforge_project:
64
- rubygems_version: 1.3.5
54
+ rubygems_version: 3.2.26
65
55
  signing_key:
66
- specification_version: 3
56
+ specification_version: 4
67
57
  summary: Traits and partial classes for Ruby
68
- test_files:
69
- - spec/spec_helper.rb
70
- - spec/does_spec.rb
71
- - spec/as_trait_spec.rb
58
+ test_files: []
data/MIT-LICENSE DELETED
@@ -1,20 +0,0 @@
1
- Copyright (c) 2009 Henning Koch
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc DELETED
@@ -1,104 +0,0 @@
1
- = modularity - Traits and partial classes for Ruby
2
-
3
- Modularity provides traits and partial classes for Ruby.
4
- This lets you organize large models into multiple source files.
5
- It also allows very simple definition of meta-programming macros,
6
- as you might now from <tt>acts_as_something</tt> type of plugins,
7
- or the macros Rails provides for your models.
8
-
9
- Modularity traits are to your models what partials are for your Rails views.
10
-
11
- == Example 1: Splitting a model into multiple source files
12
-
13
- Models are often concerned with multiple themes like "authentication", "contact info" or "permissions", each requiring
14
- a couple of validations and callbacks here, and some method there. Modularity lets you organize your model into multiple
15
- partial classes, so each file can deal with a single aspect of your model:
16
-
17
- # app/models/user.rb
18
- class User < ActiveRecord::Base
19
- does "user/authentication"
20
- does "user/address"
21
- end
22
-
23
- # app/models/user/authentication_trait.rb
24
- module User::AuthenticationTrait
25
- as_trait do
26
- # methods, validations, etc. regarding usernames and passwords go here
27
- end
28
- end
29
-
30
- # app/models/user/permissions_trait.rb
31
- module User::PermissionsTrait
32
- as_trait do
33
- # methods, validations, etc. regarding contact information go here
34
- end
35
- end
36
-
37
- == Example 2: Easy meta-programming macros
38
-
39
- Ruby allows you to construct classes using meta-programming macros like <tt>acts_as_tree</tt> or <tt>has_many :items</tt>.
40
- These macros will add methods, callbacks, etc. to the calling class. Hoever, right now Ruby (and Rails) makes it awkward to define
41
- such macros in your project as part of your application domain.
42
-
43
- Modularity allows you to extract common behaviour into reusable macros by defining traits with parameters. Your macros can live in your
44
- application, allowing you to express your application domain in both classes and macros.
45
-
46
- Here is an example of a <tt>strip_field</tt> macro, which created setter methods that remove leading and trailing whitespace from newly assigned values:
47
-
48
- # app/models/article.rb
49
- class Article
50
- does "strip_fields", :name, :brand
51
- end
52
-
53
- # app/models/shared/strip_fields_trait.rb
54
- module StripFieldsTrait
55
- as_trait do |*fields|
56
- fields.each do |field|
57
- define_method("#{field}=") do |value|
58
- self[field] = value.strip
59
- end
60
- end
61
- end
62
- end
63
-
64
- We like to add <tt>app/models/shared</tt> and <tt>app/controllers/shared</tt> to the load paths of our Rails projects. These are great places to store macros
65
- that are re-used from multiple classes.
66
-
67
- == Example 3: Mixins with class methods
68
-
69
- Using a module to add both instance methods and class methods is {very awkward}[http://redcorundum.blogspot.com/2006/06/mixing-in-class-methods.html].
70
- Modularity does away with the clutter and lets you say this:
71
-
72
- # app/models/model.rb
73
- class Model
74
- does "mixin"
75
- end
76
-
77
- # app/models/mixin_trait.rb
78
- module MixinTrait
79
- as_trait do
80
- def instance_method
81
- # ...
82
- end
83
- def self.class_method
84
- # ..
85
- end
86
- end
87
-
88
- <tt>private</tt> and <tt>protected</tt> will also work as expected when defining a trait.
89
-
90
- == Installation
91
-
92
- sudo gem sources -a http://gemcutter.org
93
- sudo gem install modularity
94
-
95
- == Dependencies
96
-
97
- Modularity requires Ruby 1.8.7. Earlier versions are missing <tt>class_exec</tt>. You might be able to hack in <tt>class_exec</tt>
98
- using {this}[http://github.com/brynary/rspec/blob/f80d61a399b34f58084a378c85a43a95ff484619/lib/spec/extensions/instance_exec.rb] as a guide, but it's not pretty.
99
-
100
- == Credits
101
-
102
- Henning Koch
103
-
104
- {www.makandra.de}[http://www.makandra.de/]
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.6.0
@@ -1,23 +0,0 @@
1
- require 'modularity/inflector'
2
- require 'modularity/as_trait'
3
-
4
- module Modularity
5
- module Does
6
-
7
- def self.included(base)
8
- base.extend ClassMethods
9
- end
10
-
11
- module ClassMethods
12
- def does(trait_name, *args)
13
- trait_name = "#{Modularity::Inflector.camelize(trait_name.to_s)}Trait"
14
- trait = Modularity::Inflector.constantize(trait_name)
15
- macro = trait.instance_variable_get("@trait_macro") or raise "Missing trait directive in #{trait_name}"
16
- class_exec(*args, &macro)
17
- end
18
- end
19
-
20
- end
21
- end
22
-
23
- Object.send :include, Modularity::Does
@@ -1,30 +0,0 @@
1
- # These methods are backported from Rails so modularity works with plain Ruby.
2
-
3
- module Modularity
4
- class Inflector
5
- class << self
6
-
7
- # File activesupport/lib/active_support/inflector.rb, line 178
8
- def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
9
- if first_letter_in_uppercase
10
- lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
11
- else
12
- lower_case_and_underscored_word.first.downcase + camelize(lower_case_and_underscored_word)[1..-1]
13
- end
14
- end
15
-
16
- # File activesupport/lib/active_support/inflector.rb, line 355
17
- def constantize(camel_cased_word)
18
- names = camel_cased_word.split('::')
19
- names.shift if names.empty? || names.first.empty?
20
-
21
- constant = Object
22
- names.each do |name|
23
- constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
24
- end
25
- constant
26
- end
27
-
28
- end
29
- end
30
- end
@@ -1,29 +0,0 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
2
-
3
- module Trait
4
- as_trait do
5
- "hi world"
6
- end
7
- end
8
-
9
- module ParametrizedTrait
10
- as_trait do |name|
11
- "hi, #{name}"
12
- end
13
- end
14
-
15
- describe Modularity::AsTrait do
16
-
17
- describe 'as_trait' do
18
-
19
- it "should let modules save a proc upon loading" do
20
- Trait.instance_variable_get("@trait_macro").call.should == "hi world"
21
- end
22
-
23
- it "should let modules save a proc with parameters upon loading" do
24
- ParametrizedTrait.instance_variable_get("@trait_macro").call("jean").should == "hi, jean"
25
- end
26
-
27
- end
28
-
29
- end
data/spec/does_spec.rb DELETED
@@ -1,93 +0,0 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
2
-
3
- module SomeTrait
4
- as_trait do
5
- some_trait_included
6
- end
7
- end
8
-
9
- module Some
10
- module ChildTrait
11
- as_trait do
12
- some_child_trait_included
13
- end
14
- end
15
- end
16
-
17
- module CallMethodTrait
18
- as_trait do |field|
19
- send(field)
20
- end
21
- end
22
-
23
- module VisibilityTrait
24
- as_trait do
25
- def public_method_from_trait
26
- end
27
- protected
28
- def protected_method_from_trait
29
- end
30
- private
31
- def private_method_from_trait
32
- end
33
- end
34
- end
35
-
36
- module DefineConstantMethodTrait
37
- as_trait do |name, return_value|
38
- define_method name do
39
- return_value
40
- end
41
- end
42
- end
43
-
44
- class Doer
45
- end
46
-
47
- describe Modularity::AsTrait do
48
-
49
- describe 'does' do
50
-
51
- it "should apply the named module" do
52
- Doer.should_receive(:some_trait_included)
53
- Doer.class_eval do
54
- does "some"
55
- end
56
- end
57
-
58
- it "should apply a namespaced module, using slash-notation like require" do
59
- Doer.should_receive(:some_child_trait_included)
60
- Doer.class_eval do
61
- does "some/child"
62
- end
63
- end
64
-
65
- it "should class_eval the as_trait proc on the doer" do
66
- Doer.should_receive(:foo)
67
- Doer.class_eval do
68
- does "call_method", :foo
69
- end
70
- end
71
-
72
- it "should allow the trait to define methods with different visibility" do
73
- Doer.class_eval do
74
- does "visibility"
75
- end
76
- instance = Doer.new
77
- instance.public_methods.should include("public_method_from_trait")
78
- instance.protected_methods.should include("protected_method_from_trait")
79
- instance.private_methods.should include("private_method_from_trait")
80
- end
81
-
82
- it "should allow the trait to perform metaprogramming acrobatics" do
83
- Doer.class_eval do
84
- does "define_constant_method", "some_method", "some_return_value"
85
- end
86
- instance = Doer.new
87
- instance.should respond_to(:some_method)
88
- instance.some_method.should == "some_return_value"
89
- end
90
-
91
- end
92
-
93
- end
data/spec/rcov.opts DELETED
@@ -1,2 +0,0 @@
1
- --exclude "spec/*,gems/*"
2
- --rails
data/spec/spec.opts DELETED
@@ -1,4 +0,0 @@
1
- --colour
2
- --format progress
3
- --loadby mtime
4
- --reverse
data/spec/spec_helper.rb DELETED
@@ -1,3 +0,0 @@
1
- $: << File.join(File.dirname(__FILE__), "/../lib" )
2
-
3
- require "#{File.dirname(__FILE__)}/../lib/modularity"