modularity 0.6.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/test.yml +36 -0
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +22 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +47 -0
- data/LICENSE.txt +21 -0
- data/README.md +128 -0
- data/Rakefile +4 -36
- data/bin/console +9 -0
- data/lib/modularity/as_trait.rb +34 -3
- data/lib/modularity/version.rb +3 -0
- data/lib/modularity.rb +1 -2
- data/modularity.gemspec +22 -51
- metadata +38 -51
- data/MIT-LICENSE +0 -20
- data/README.rdoc +0 -104
- data/VERSION +0 -1
- data/lib/modularity/does.rb +0 -23
- data/lib/modularity/inflector.rb +0 -30
- data/spec/as_trait_spec.rb +0 -29
- data/spec/does_spec.rb +0 -93
- data/spec/rcov.opts +0 -2
- data/spec/spec.opts +0 -4
- data/spec/spec_helper.rb +0 -3
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
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
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 '
|
2
|
-
|
3
|
-
require '
|
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
data/lib/modularity/as_trait.rb
CHANGED
@@ -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
|
-
|
4
|
-
|
18
|
+
|
19
|
+
def as_trait(¯o)
|
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
|
-
|
40
|
+
Module.send(:include, Modularity::AsTrait)
|
data/lib/modularity.rb
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
require 'modularity/
|
2
|
-
|
1
|
+
require 'modularity/as_trait'
|
data/modularity.gemspec
CHANGED
@@ -1,56 +1,27 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
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 |
|
7
|
-
|
8
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
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.
|
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:
|
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:
|
14
|
+
email:
|
15
|
+
- henning.koch@makandra.de
|
18
16
|
executables: []
|
19
|
-
|
20
17
|
extensions: []
|
21
|
-
|
22
|
-
|
23
|
-
-
|
24
|
-
|
25
|
-
- .
|
26
|
-
-
|
27
|
-
-
|
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
|
-
-
|
30
|
+
- bin/console
|
30
31
|
- lib/modularity.rb
|
31
32
|
- lib/modularity/as_trait.rb
|
32
|
-
- lib/modularity/
|
33
|
-
- lib/modularity/inflector.rb
|
33
|
+
- lib/modularity/version.rb
|
34
34
|
- modularity.gemspec
|
35
|
-
|
36
|
-
|
37
|
-
-
|
38
|
-
|
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
|
-
|
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:
|
54
|
-
|
55
|
-
|
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:
|
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:
|
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
|
data/lib/modularity/does.rb
DELETED
@@ -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, ¯o)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
Object.send :include, Modularity::Does
|
data/lib/modularity/inflector.rb
DELETED
@@ -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
|
data/spec/as_trait_spec.rb
DELETED
@@ -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
data/spec/spec.opts
DELETED
data/spec/spec_helper.rb
DELETED