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 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"