From fce594cbb88efc27a3fe2c70dc8c5a6f1a032847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20Kr=C3=A4uter?= Date: Wed, 27 Nov 2013 11:15:13 +0100 Subject: [PATCH 01/32] just a little detail: missing end --- README.rdoc | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rdoc b/README.rdoc index 3190ea7..2f385fe 100755 --- a/README.rdoc +++ b/README.rdoc @@ -58,6 +58,7 @@ Modularity does away with the clutter and lets you say this: def self.class_method # .. end + end end private and protected will also work as expected when defining a trait. From c72700673c7f312165b5c3545dd593d8adefec82 Mon Sep 17 00:00:00 2001 From: Henning Koch Date: Wed, 8 Jan 2014 13:53:06 +0100 Subject: [PATCH 02/32] Modularity 2 syntax --- lib/modularity.rb | 3 +- lib/modularity/as_trait.rb | 37 ++++++++++- lib/modularity/does.rb | 23 ------- lib/modularity/inflector.rb | 60 ------------------ modularity.gemspec | 1 + spec/as_trait_spec.rb | 121 +++++++++++++++++++++++++++++++----- spec/does_spec.rb | 93 --------------------------- spec/spec_helper.rb | 1 + 8 files changed, 141 insertions(+), 198 deletions(-) delete mode 100755 lib/modularity/does.rb delete mode 100755 lib/modularity/inflector.rb delete mode 100755 spec/does_spec.rb diff --git a/lib/modularity.rb b/lib/modularity.rb index 39e4ef5..402ce75 100755 --- a/lib/modularity.rb +++ b/lib/modularity.rb @@ -1,2 +1 @@ -require 'modularity/does' - +require 'modularity/as_trait' diff --git a/lib/modularity/as_trait.rb b/lib/modularity/as_trait.rb index f26ae95..f1145bd 100755 --- a/lib/modularity/as_trait.rb +++ b/lib/modularity/as_trait.rb @@ -1,9 +1,40 @@ module Modularity + + class ParametrizedTrait < Module + + def initialize(blank_trait, args) + @args = args + @macro = blank_trait.instance_variable_get(:@modularity_macro) + include(blank_trait) + end + + def included(base) + base.class_exec(*@args, &@macro) + end + + end + module AsTrait - def as_trait(&block) - @trait_macro = block + + def as_trait(¯o) + + @modularity_macro = macro + + def self.included(base) + unless base.is_a?(ParametrizedTrait) + base.class_exec(&@modularity_macro) + end + + end + + def self.[](*args) + blank_trait = self + ParametrizedTrait.new(blank_trait, args) + end + end + end end -Object.send :include, Modularity::AsTrait +Module.send(:include, Modularity::AsTrait) diff --git a/lib/modularity/does.rb b/lib/modularity/does.rb deleted file mode 100755 index c14b7c2..0000000 --- a/lib/modularity/does.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'modularity/inflector' -require 'modularity/as_trait' - -module Modularity - module Does - - def self.included(base) - base.extend ClassMethods - end - - module ClassMethods - def does(trait_name, *args) - trait_name = "#{Modularity::Inflector.camelize(trait_name.to_s)}Trait" - trait = Modularity::Inflector.constantize(trait_name) - macro = trait.instance_variable_get("@trait_macro") or raise "Missing trait directive in #{trait_name}" - class_exec(*args, ¯o) - end - end - - end -end - -Object.send :include, Modularity::Does diff --git a/lib/modularity/inflector.rb b/lib/modularity/inflector.rb deleted file mode 100755 index 5706eee..0000000 --- a/lib/modularity/inflector.rb +++ /dev/null @@ -1,60 +0,0 @@ -# These methods are backported from Rails so modularity works with plain Ruby. - -module Modularity - class Inflector - class << self - - # File activesupport/lib/active_support/inflector.rb, line 178 - def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true) - if first_letter_in_uppercase - lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } - else - lower_case_and_underscored_word.first.downcase + camelize(lower_case_and_underscored_word)[1..-1] - end - end - - - if Module.method(:const_get).arity == 1 - # Tries to find a constant with the name specified in the argument string: - # - # "Module".constantize # => Module - # "Test::Unit".constantize # => Test::Unit - # - # The name is assumed to be the one of a top-level constant, no matter whether - # it starts with "::" or not. No lexical context is taken into account: - # - # C = 'outside' - # module M - # C = 'inside' - # C # => 'inside' - # "C".constantize # => 'outside', same as ::C - # end - # - # NameError is raised when the name is not in CamelCase or the constant is - # unknown. - def constantize(camel_cased_word) - names = camel_cased_word.split('::') - names.shift if names.empty? || names.first.empty? - - constant = Object - names.each do |name| - constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name) - end - constant - end - else - def constantize(camel_cased_word) #:nodoc: - names = camel_cased_word.split('::') - names.shift if names.empty? || names.first.empty? - - constant = Object - names.each do |name| - constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name) - end - constant - end - end - - end - end -end diff --git a/modularity.gemspec b/modularity.gemspec index 89a9fd0..ac5015b 100755 --- a/modularity.gemspec +++ b/modularity.gemspec @@ -18,5 +18,6 @@ Gem::Specification.new do |s| s.add_development_dependency('rake') s.add_development_dependency('rspec', '<2') + s.add_development_dependency('rspec_candy') end diff --git a/spec/as_trait_spec.rb b/spec/as_trait_spec.rb index 48383ea..a47b67a 100755 --- a/spec/as_trait_spec.rb +++ b/spec/as_trait_spec.rb @@ -1,29 +1,116 @@ require 'spec_helper' -module Trait - as_trait do - "hi world" - end -end +describe Modularity::AsTrait do -module ParametrizedTrait - as_trait do |name| - "hi, #{name}" - end -end + describe '.included' do + + before :each do + @doing_class = Class.new + end -describe Modularity::AsTrait do + describe 'without parameters' do + + it "applies the trait macro of the given module" do + + module DoesSome + as_trait do + some_trait_included + end + end + + @doing_class.should_receive(:some_trait_included) + + @doing_class.class_eval do + include DoesSome + end + + end + + it "applies the trait macro of the given namespaced module" do + + module Some + module DoesChild + as_trait do + some_child_trait_included + end + end + end + + @doing_class.should_receive(:some_child_trait_included) + + @doing_class.class_eval do + include Some::DoesChild + end + + end - describe 'as_trait' do + it "lets a trait define methods with different visibility" do + + module DoesVisibility + as_trait do + def public_method_from_trait + end + protected + def protected_method_from_trait + end + private + def private_method_from_trait + end + end + end + + @doing_class.class_eval do + include DoesVisibility + end + + instance = @doing_class.new + + instance.public_methods.collect(&:to_s).should include("public_method_from_trait") + instance.protected_methods.collect(&:to_s).should include("protected_method_from_trait") + instance.private_methods.collect(&:to_s).should include("private_method_from_trait") + + end - it "should let modules save a proc upon loading" do - Trait.instance_variable_get("@trait_macro").call.should == "hi world" end - it "should let modules save a proc with parameters upon loading" do - ParametrizedTrait.instance_variable_get("@trait_macro").call("jean").should == "hi, jean" + describe "with parameters" do + + it "it applies a trait macro with parameters" do + + module DoesCallMethod + as_trait do |field| + send(field) + end + end + + @doing_class.should_receive(:foo) + @doing_class.class_eval do + include DoesCallMethod[:foo] + end + + end + + it "facilitates metaprogramming acrobatics" do + + module DoesDefineConstantMethod + as_trait do |name, return_value| + define_method name do + return_value + end + end + end + + @doing_class.class_eval do + include DoesDefineConstantMethod["some_method", "some_return_value"] + end + + instance = @doing_class.new + instance.should respond_to(:some_method) + instance.some_method.should == "some_return_value" + end + end end - + end diff --git a/spec/does_spec.rb b/spec/does_spec.rb deleted file mode 100755 index a918e86..0000000 --- a/spec/does_spec.rb +++ /dev/null @@ -1,93 +0,0 @@ -require 'spec_helper' - -module SomeTrait - as_trait do - some_trait_included - end -end - -module Some - module ChildTrait - as_trait do - some_child_trait_included - end - end -end - -module CallMethodTrait - as_trait do |field| - send(field) - end -end - -module VisibilityTrait - as_trait do - def public_method_from_trait - end - protected - def protected_method_from_trait - end - private - def private_method_from_trait - end - end -end - -module DefineConstantMethodTrait - as_trait do |name, return_value| - define_method name do - return_value - end - end -end - -class Doer -end - -describe Modularity::AsTrait do - - describe 'does' do - - it "should apply the named module" do - Doer.should_receive(:some_trait_included) - Doer.class_eval do - does "some" - end - end - - it "should apply a namespaced module, using slash-notation like require" do - Doer.should_receive(:some_child_trait_included) - Doer.class_eval do - does "some/child" - end - end - - it "should class_eval the as_trait proc on the doer" do - Doer.should_receive(:foo) - Doer.class_eval do - does "call_method", :foo - end - end - - it "should allow the trait to define methods with different visibility" do - Doer.class_eval do - does "visibility" - end - instance = Doer.new - instance.public_methods.collect(&:to_s).should include("public_method_from_trait") - instance.protected_methods.collect(&:to_s).should include("protected_method_from_trait") - instance.private_methods.collect(&:to_s).should include("private_method_from_trait") - end - - it "should allow the trait to perform metaprogramming acrobatics" do - Doer.class_eval do - does "define_constant_method", "some_method", "some_return_value" - end - instance = Doer.new - instance.should respond_to(:some_method) - instance.some_method.should == "some_return_value" - end - - end - -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9669a3f..85aa730 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,4 @@ $: << File.join(File.dirname(__FILE__), "/../lib" ) require "#{File.dirname(__FILE__)}/../lib/modularity" +require 'rspec_candy/all' From 0e4d72146931f179b479820f22e4174f2404d5cc Mon Sep 17 00:00:00 2001 From: Henning Koch Date: Wed, 8 Jan 2014 13:57:39 +0100 Subject: [PATCH 03/32] Add tests to ensure that including a traited module appends methods outside the trait macro --- spec/as_trait_spec.rb | 66 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/spec/as_trait_spec.rb b/spec/as_trait_spec.rb index a47b67a..bf120cb 100755 --- a/spec/as_trait_spec.rb +++ b/spec/as_trait_spec.rb @@ -71,6 +71,31 @@ def private_method_from_trait end + it 'appends methods outside the trait macro' do + + module HybridModule + + as_trait do + define_method :trait_method do + end + end + + def vanilla_method + end + + end + + @doing_class.class_eval do + include HybridModule + end + + instance = @doing_class.new + + instance.should respond_to(:trait_method) + instance.should respond_to(:vanilla_method) + + end + end describe "with parameters" do @@ -109,6 +134,47 @@ module DoesDefineConstantMethod instance.some_method.should == "some_return_value" end + it "allies to call an unparametrized trait macro with an empty parameter list" do + + module DoesSome + as_trait do + some_trait_included + end + end + + @doing_class.should_receive(:some_trait_included) + + @doing_class.class_eval do + include DoesSome[] + end + + end + + it 'appends methods outside the trait macro' do + + module HybridModuleWithParameters + + as_trait do |name| + define_method name do + end + end + + def vanilla_method + end + + end + + @doing_class.class_eval do + include HybridModuleWithParameters[:trait_method] + end + + instance = @doing_class.new + + instance.should respond_to(:trait_method) + instance.should respond_to(:vanilla_method) + + end + end end From 8e0faf57b7ee6827c15acc9cdf9d3986fcaaa029 Mon Sep 17 00:00:00 2001 From: Henning Koch Date: Wed, 8 Jan 2014 14:01:02 +0100 Subject: [PATCH 04/32] test to assert that multiple traits can be applied --- spec/as_trait_spec.rb | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/spec/as_trait_spec.rb b/spec/as_trait_spec.rb index bf120cb..37917e3 100755 --- a/spec/as_trait_spec.rb +++ b/spec/as_trait_spec.rb @@ -96,6 +96,34 @@ def vanilla_method end + it 'applies multiple trait macros' do + + module FirstTrait + as_trait do + define_method :first do + end + end + end + + module SecondTrait + as_trait do + define_method :second do + end + end + end + + @doing_class.class_eval do + include FirstTrait + include SecondTrait + end + + instance = @doing_class.new + + instance.should respond_to(:first) + instance.should respond_to(:second) + + end + end describe "with parameters" do From 55eadcdbd40a94867e12f0c71f7e7112f56ad87f Mon Sep 17 00:00:00 2001 From: Henning Koch Date: Wed, 8 Jan 2014 17:10:35 +0100 Subject: [PATCH 05/32] migration script modularity1 => 2 --- bin/migrate-modularity1-to-modularity2 | 7 ++ lib/modularity/migrator.rb | 49 +++++++++++ spec/{ => modularity}/as_trait_spec.rb | 0 spec/modularity/migrator_spec.rb | 109 +++++++++++++++++++++++++ 4 files changed, 165 insertions(+) create mode 100755 bin/migrate-modularity1-to-modularity2 create mode 100644 lib/modularity/migrator.rb rename spec/{ => modularity}/as_trait_spec.rb (100%) create mode 100644 spec/modularity/migrator_spec.rb diff --git a/bin/migrate-modularity1-to-modularity2 b/bin/migrate-modularity1-to-modularity2 new file mode 100755 index 0000000..2afb239 --- /dev/null +++ b/bin/migrate-modularity1-to-modularity2 @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +require File.expand_path(File.dirname(__FILE__)) + '/../lib/modularity/migrator' + +ARGV.each do |path| + Modularity::Migrator.migrate(path) +end diff --git a/lib/modularity/migrator.rb b/lib/modularity/migrator.rb new file mode 100644 index 0000000..f97d9cd --- /dev/null +++ b/lib/modularity/migrator.rb @@ -0,0 +1,49 @@ +gem 'activesupport' +require 'active_support/all' + +module Modularity + class Migrator + class << self + + def migrate(old_path) + new_path = fix_filename(old_path) + rename(old_path, new_path) unless old_path == new_path + old_code = File.read(new_path) + new_code = fix_code(old_code) + rewrite_file(new_path, new_code) unless old_path == new_path + new_code + end + + private + + def fix_filename(path) + path = File.expand_path(path) + new_path = path.sub(/\/([^\/]+)_trait\.rb$/, '/does_\\1.rb') + new_path + end + + def fix_code(code) + code = code.gsub(/module (.*?)([A-Za-z0-9_]+)Trait\b/, 'module \\1Does\\2') + code = code.gsub(/does ['":]([A-Za-z0-9\_\/]+)(?:'|"|$)(?:,\s*(.*)$)?/) do + trait_path = $1 + parameters = $2 + trait_path = trait_path.sub(/([A-Za-z0-9\_]+)$/, 'does_\\1') + trait_class = trait_path.camelize # don't use classify, it removes plurals! + substituted = "include #{trait_class}" + substituted << "[#{parameters}]" if parameters + substituted + end + code + end + + def rename(old, new) + FileUtils.mv(old, enw) + end + + def rewrite_file(path, content) + File.open(path, "w") { |file| file.write(content) } + end + + end + end +end diff --git a/spec/as_trait_spec.rb b/spec/modularity/as_trait_spec.rb similarity index 100% rename from spec/as_trait_spec.rb rename to spec/modularity/as_trait_spec.rb diff --git a/spec/modularity/migrator_spec.rb b/spec/modularity/migrator_spec.rb new file mode 100644 index 0000000..82c196a --- /dev/null +++ b/spec/modularity/migrator_spec.rb @@ -0,0 +1,109 @@ +require 'spec_helper' + +require 'modularity/migrator' # this is an optional file and thus not loaded automatically + +describe Modularity::Migrator do + + subject do + Modularity::Migrator + end + + describe '.fix_filename' do + + it 'should replace the suffix _trait with a prefix does' do + subject.send(:fix_filename, '/path/to/search_trait.rb').should == '/path/to/does_search.rb' + end + + it "should not rename files that don't end in _trait.rb" do + subject.send(:fix_filename, '/path/to/search.rb').should == '/path/to/search.rb' + end + + end + + describe '.fix_code' do + + it 'renames a module FooBarTrait to DoesFooBar' do + + old_code = <<-RUBY + module Namespace::FooBarTrait + as_trait do + define_method :foo do + end + define_method :bar do + end + end + end + RUBY + + new_code = <<-RUBY + module Namespace::DoesFooBar + as_trait do + define_method :foo do + end + define_method :bar do + end + end + end + RUBY + + subject.send(:fix_code, old_code).should == new_code + end + + it "does not rename modules that aren't traits" do + + old_code = <<-RUBY + module Namespace::FooBar + def foo + end + def bar + end + end + RUBY + + subject.send(:fix_code, old_code).should == old_code + end + + it 'replaces does calls with include' do + + old_code = <<-RUBY + class User < ActiveRecord::Base + does 'user/search' + does 'user/account_settings' + does 'trashable' + end + RUBY + + new_code = <<-RUBY + class User < ActiveRecord::Base + include User::DoesSearch + include User::DoesAccountSettings + include DoesTrashable + end + RUBY + + subject.send(:fix_code, old_code).should == new_code + end + + it 'puts does parameters into square brackets' do + + old_code = <<-RUBY + class User < ActiveRecord::Base + does 'flag', :active, :default => true + does 'record/search', :field => :email + end + RUBY + + new_code = <<-RUBY + class User < ActiveRecord::Base + include DoesFlag[:active, :default => true] + include Record::DoesSearch[:field => :email] + end + RUBY + + subject.send(:fix_code, old_code).should == new_code + + end + + end + +end \ No newline at end of file From 22354692b72e9b1b3fe8c205216809a98195fd0d Mon Sep 17 00:00:00 2001 From: Henning Koch Date: Wed, 8 Jan 2014 18:07:31 +0100 Subject: [PATCH 06/32] Bump version, rewrite README in markdown and for version 2, fix migration script --- README.md | 122 +++++++++++++++++++++++++++++++ README.rdoc | 107 --------------------------- lib/modularity/migrator.rb | 8 +- lib/modularity/version.rb | 2 +- spec/modularity/migrator_spec.rb | 2 +- 5 files changed, 130 insertions(+), 111 deletions(-) create mode 100755 README.md delete mode 100755 README.rdoc diff --git a/README.md b/README.md new file mode 100755 index 0000000..7fd4fbb --- /dev/null +++ b/README.md @@ -0,0 +1,122 @@ +Modularity 2 - Traits and partial classes for Ruby +================================================== + +Modularity enhances Ruby's [`Module`] so it can be used traits and partial classes. +This allows very simple definition of meta-programming macros like the +`has_many` that you know from Rails. + +Modularity also lets you organize large models into multiple source files +in a way that is less awkward than using modules. + +Note that this is **Modularity 2**, which has a different syntax older version. +Modularity 1 users can use [a script to migrate their code](#migrating-from-modularity-1) +or use the [modularity1 branch](https://github.com/makandra/modularity/tree/modularity1). + + +Example 1: Easy meta-programming macros +---------------------------------------- + +Ruby allows you to construct classes using meta-programming macros like +`acts_as_tree` or `has_many :items`. These macros will add methods, +callbacks, etc. to the calling class. However, right now Ruby (and Rails) makes it awkward to define +such macros in your project as part of your application domain. + +Modularity allows you to extract common behaviour into reusable macros by defining traits with parameters. +Your macros can live in your application, allowing you to express your application domain in both classes +and macros. + +Here is an example of a `strip_field` macro, which created setter methods that remove leading and trailing whitespace from newly assigned values: + + # app/models/article.rb + class Article + include DoesStripFields[:name, :brand] + end + + # app/models/shared/does_strip_fields.rb + module DoesStripFields + as_trait do |*fields| + fields.each do |field| + define_method("#{field}=") do |value| + self[field] = value.strip + end + end + end + end + +We like to add `app/models/shared` and `app/controllers/shared` to the load paths of our Rails projects. +These are great places to store macros that are re-used from multiple classes. + + +Example 2: Mixins with class methods +------------------------------------ + +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). +Modularity does away with the clutter and lets you say this: + + # app/models/model.rb + class Model + include Mixin + end + + # app/models/mixin.rb + module Mixin + as_trait do + def instance_method + # ... + end + def self.class_method + # .. + end + end + end + +`private` and `protected` will also work as expected when defining a trait. + + +Example 3: Splitting a model into multiple source files +------------------------------------------------------- + +Models are often concerned with multiple themes like "authentication", "contact info" or "permissions", each requiring +a couple of validations and callbacks here, and some method there. Modularity lets you organize your model into multiple +partial classes, so each file can deal with a single aspect of your model: + + # app/models/user.rb + class User < ActiveRecord::Base + include User::DoesAuthentication + include User::DoesAddress + end + + # app/models/user/does_authentication.rb + module User::DoesAuthentication + as_trait do + # methods, validations, etc. regarding usernames and passwords go here + end + end + + # app/models/user/does_permissions.rb + module User::DoesPermissions + as_trait do + # methods, validations, etc. regarding contact information go here + end + end + +Some criticism has been raised for splitting large models into files like this. +Essentially, even though have an easier time navigating your code, you will still +have one giant model with many side effects. + +There are [many better ways](http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/) +to decompose a huge Ruby class. + + +Installation +------------ + + sudo gem install modularity + + +Credits +------- + +Henning Koch from [makandra.com](http://makandra.com/) + diff --git a/README.rdoc b/README.rdoc deleted file mode 100755 index 3190ea7..0000000 --- a/README.rdoc +++ /dev/null @@ -1,107 +0,0 @@ -= modularity - Traits and partial classes for Ruby - -Modularity provides traits and partial classes for Ruby. -This allows very simple definition of meta-programming macros, -as you might now from acts_as_something type of plugins, -or the macros Rails provides for your models. This also lets you organize -large models into multiple source files in a way that is less awkward -than using modules. - -Modularity traits are to your models what partials are for your Rails views. - -== Example 1: Easy meta-programming macros - -Ruby allows you to construct classes using meta-programming macros like acts_as_tree or has_many :items. -These macros will add methods, callbacks, etc. to the calling class. However, right now Ruby (and Rails) makes it awkward to define -such macros in your project as part of your application domain. - -Modularity allows you to extract common behaviour into reusable macros by defining traits with parameters. Your macros can live in your -application, allowing you to express your application domain in both classes and macros. - -Here is an example of a strip_field macro, which created setter methods that remove leading and trailing whitespace from newly assigned values: - - # app/models/article.rb - class Article - does "strip_fields", :name, :brand - end - - # app/models/shared/strip_fields_trait.rb - module StripFieldsTrait - as_trait do |*fields| - fields.each do |field| - define_method("#{field}=") do |value| - self[field] = value.strip - end - end - end - end - -We like to add app/models/shared and app/controllers/shared to the load paths of our Rails projects. These are great places to store macros -that are re-used from multiple classes. - -== Example 2: Mixins with class methods - -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]. -Modularity does away with the clutter and lets you say this: - - # app/models/model.rb - class Model - does "mixin" - end - - # app/models/mixin_trait.rb - module MixinTrait - as_trait do - def instance_method - # ... - end - def self.class_method - # .. - end - end - -private and protected will also work as expected when defining a trait. - -== Example 3: Splitting a model into multiple source files - -Models are often concerned with multiple themes like "authentication", "contact info" or "permissions", each requiring -a couple of validations and callbacks here, and some method there. Modularity lets you organize your model into multiple -partial classes, so each file can deal with a single aspect of your model: - - # app/models/user.rb - class User < ActiveRecord::Base - does "user/authentication" - does "user/address" - end - - # app/models/user/authentication_trait.rb - module User::AuthenticationTrait - as_trait do - # methods, validations, etc. regarding usernames and passwords go here - end - end - - # app/models/user/permissions_trait.rb - module User::PermissionsTrait - as_trait do - # methods, validations, etc. regarding contact information go here - end - end - -== Installation - - sudo gem install modularity - - -== Note if you're still on Ruby 1.8.6 - -Modularity requires Ruby 1.8.7. Earlier versions are missing class_exec. You might be able to hack in class_exec -using {this}[http://github.com/brynary/rspec/blob/f80d61a399b34f58084a378c85a43a95ff484619/lib/spec/extensions/instance_exec.rb] as a guide, but it's not pretty. - -== Credits - -Henning Koch - -{makandra.com}[http://makandra.com/] - -{gem-session.com}[http://gem-session.com/] diff --git a/lib/modularity/migrator.rb b/lib/modularity/migrator.rb index f97d9cd..ed8733b 100644 --- a/lib/modularity/migrator.rb +++ b/lib/modularity/migrator.rb @@ -1,16 +1,20 @@ gem 'activesupport' require 'active_support/all' +require 'fileutils' module Modularity class Migrator class << self def migrate(old_path) + old_path = File.expand_path(old_path) new_path = fix_filename(old_path) rename(old_path, new_path) unless old_path == new_path old_code = File.read(new_path) new_code = fix_code(old_code) - rewrite_file(new_path, new_code) unless old_path == new_path + rewrite_file(new_path, new_code) unless old_code == new_code + puts "Migrated #{old_path}" + `ruby -c #{new_path}` new_code end @@ -37,7 +41,7 @@ def fix_code(code) end def rename(old, new) - FileUtils.mv(old, enw) + FileUtils.mv(old, new) end def rewrite_file(path, content) diff --git a/lib/modularity/version.rb b/lib/modularity/version.rb index b84bf07..3c9469c 100644 --- a/lib/modularity/version.rb +++ b/lib/modularity/version.rb @@ -1,3 +1,3 @@ module Modularity - VERSION = '0.6.1' + VERSION = '2.0.0' end diff --git a/spec/modularity/migrator_spec.rb b/spec/modularity/migrator_spec.rb index 82c196a..05ce32a 100644 --- a/spec/modularity/migrator_spec.rb +++ b/spec/modularity/migrator_spec.rb @@ -106,4 +106,4 @@ class User < ActiveRecord::Base end -end \ No newline at end of file +end From 5ae0dc9dabef1f3a43f08c159bb2999af1848d11 Mon Sep 17 00:00:00 2001 From: Henning Koch Date: Wed, 8 Jan 2014 18:11:46 +0100 Subject: [PATCH 07/32] Document migration script --- README.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7fd4fbb..5928e2b 100755 --- a/README.md +++ b/README.md @@ -112,7 +112,33 @@ to decompose a huge Ruby class. Installation ------------ - sudo gem install modularity +Add the following to your `Gemfile`: + + gem 'modularity', '>=2' + +Now run `bundle install`. + + +Migrating from Modularity 1 +--------------------------- + +If you have been using Modularity 1 with the `does` syntax, we provide a script to migrate your Ruby project +automatically. + +1. Make sure your project has tests and you have a backup of your files (or pushed your commits to Git) + +2. From your project directory, do this: + + find . -name "*.rb" | migrate-modularity1-to-modularity2 + +3. The script will rename your files and change your code. It will also syntax-check your files after conversion + (since the script is not perfect). + +4. Check the diff to see what the script has done. + +5. Run tests to see if everything still works. + + Credits From e9df53ccec48696f636f32cc688cf8df4c60a6af Mon Sep 17 00:00:00 2001 From: Henning Koch Date: Wed, 8 Jan 2014 18:17:23 +0100 Subject: [PATCH 08/32] fix README --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5928e2b..33e4d6e 100755 --- a/README.md +++ b/README.md @@ -42,7 +42,9 @@ Here is an example of a `strip_field` macro, which created setter methods that r end end end - + +Notice the `as_trait` block. + We like to add `app/models/shared` and `app/controllers/shared` to the load paths of our Rails projects. These are great places to store macros that are re-used from multiple classes. @@ -84,7 +86,7 @@ partial classes, so each file can deal with a single aspect of your model: # app/models/user.rb class User < ActiveRecord::Base include User::DoesAuthentication - include User::DoesAddress + include User::DoesPermissions end # app/models/user/does_authentication.rb From cb6ab2121829f1eb7a60c47f5ff5d4e1e4ff260f Mon Sep 17 00:00:00 2001 From: Henning Koch Date: Thu, 9 Jan 2014 13:40:53 +0100 Subject: [PATCH 09/32] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 33e4d6e..725fdc0 100755 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ and macros. Here is an example of a `strip_field` macro, which created setter methods that remove leading and trailing whitespace from newly assigned values: # app/models/article.rb - class Article + class Article < ActiveRecord::Base include DoesStripFields[:name, :brand] end @@ -85,8 +85,8 @@ partial classes, so each file can deal with a single aspect of your model: # app/models/user.rb class User < ActiveRecord::Base - include User::DoesAuthentication - include User::DoesPermissions + include DoesAuthentication + include DoesPermissions end # app/models/user/does_authentication.rb From 971873f616814fe9fa708c72572b6c812b96c32d Mon Sep 17 00:00:00 2001 From: Henning Koch Date: Mon, 13 Jan 2014 08:14:12 +0100 Subject: [PATCH 10/32] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 725fdc0..1fd55af 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Modularity 2 - Traits and partial classes for Ruby ================================================== -Modularity enhances Ruby's [`Module`] so it can be used traits and partial classes. +Modularity enhances Ruby's [`Module`](http://apidock.com/ruby/Module) so it can be used traits and partial classes. This allows very simple definition of meta-programming macros like the `has_many` that you know from Rails. From df8ee3b0af227086b8f6b34432e2ec8f9ae3b3b3 Mon Sep 17 00:00:00 2001 From: Tobias Kraze Date: Tue, 4 Feb 2014 12:23:28 +0100 Subject: [PATCH 11/32] fix migration script to read filenames from STDIN --- bin/migrate-modularity1-to-modularity2 | 4 ++-- lib/modularity/version.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/migrate-modularity1-to-modularity2 b/bin/migrate-modularity1-to-modularity2 index 2afb239..3bb149e 100755 --- a/bin/migrate-modularity1-to-modularity2 +++ b/bin/migrate-modularity1-to-modularity2 @@ -2,6 +2,6 @@ require File.expand_path(File.dirname(__FILE__)) + '/../lib/modularity/migrator' -ARGV.each do |path| - Modularity::Migrator.migrate(path) +ARGF.each do |path| + Modularity::Migrator.migrate(path.strip) end diff --git a/lib/modularity/version.rb b/lib/modularity/version.rb index 3c9469c..e8322d8 100644 --- a/lib/modularity/version.rb +++ b/lib/modularity/version.rb @@ -1,3 +1,3 @@ module Modularity - VERSION = '2.0.0' + VERSION = '2.0.1' end From 7e3ccaf6121b7332b1a15695ba1cba2374457368 Mon Sep 17 00:00:00 2001 From: Simon Stemplinger Date: Sun, 9 Nov 2014 12:09:26 +0100 Subject: [PATCH 12/32] add license to gemspec --- modularity.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/modularity.gemspec b/modularity.gemspec index ac5015b..f479b00 100755 --- a/modularity.gemspec +++ b/modularity.gemspec @@ -5,6 +5,7 @@ require 'modularity/version' Gem::Specification.new do |s| s.name = %q{modularity} s.version = Modularity::VERSION + s.licenses = ["MIT"] s.authors = ["Henning Koch"] s.email = %q{github@makandra.de} s.homepage = %q{http://github.com/makandra/modularity} From 91cc0d6901572e44dc3159f1863b986f14b0b52d Mon Sep 17 00:00:00 2001 From: Emanuel Denzel Date: Tue, 7 Aug 2018 17:52:11 +0200 Subject: [PATCH 13/32] Add a CHANGELOG --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..67685cc --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- This CHANGELOG file. + From d93f2366dec942f0c30d5936f852e4eb97232d21 Mon Sep 17 00:00:00 2001 From: Emanuel Denzel Date: Thu, 23 Aug 2018 17:19:29 +0200 Subject: [PATCH 14/32] Update the changelog to format https://makandracards.com/makandra/54223-how-to-write-a-good-changelog --- CHANGELOG.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67685cc..cda8844 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,10 @@ # Changelog All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [Unreleased] -### Added -- This CHANGELOG file. +## Unreleased +### Compatible changes +- This CHANGELOG file. From efa8809a8b529551e09ebb3f5278bbcdac62335e Mon Sep 17 00:00:00 2001 From: Emanuel Denzel Date: Tue, 24 Aug 2021 15:02:40 +0200 Subject: [PATCH 15/32] Remove migration code for upgrading to version 2 --- CHANGELOG.md | 5 +- README.md | 160 ++++++++++--------------- bin/migrate-modularity1-to-modularity2 | 7 -- lib/modularity/migrator.rb | 53 -------- spec/modularity/migrator_spec.rb | 109 ----------------- 5 files changed, 69 insertions(+), 265 deletions(-) delete mode 100755 bin/migrate-modularity1-to-modularity2 delete mode 100644 lib/modularity/migrator.rb delete mode 100644 spec/modularity/migrator_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index cda8844..5638170 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - ## Unreleased +### Breaking changes + +- Removed migration guide for modularity version 1. + ### Compatible changes - This CHANGELOG file. diff --git a/README.md b/README.md index 1fd55af..43353d7 100755 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -Modularity 2 - Traits and partial classes for Ruby -================================================== +# Modularity 2 - Traits and partial classes for Ruby Modularity enhances Ruby's [`Module`](http://apidock.com/ruby/Module) so it can be used traits and partial classes. This allows very simple definition of meta-programming macros like the @@ -8,13 +7,17 @@ This allows very simple definition of meta-programming macros like the Modularity also lets you organize large models into multiple source files in a way that is less awkward than using modules. -Note that this is **Modularity 2**, which has a different syntax older version. -Modularity 1 users can use [a script to migrate their code](#migrating-from-modularity-1) -or use the [modularity1 branch](https://github.com/makandra/modularity/tree/modularity1). +## Installation +Add the following to your `Gemfile`: + +``` +gem 'modularity' +``` -Example 1: Easy meta-programming macros ----------------------------------------- +Now run `bundle install`. + +## Example 1: Easy meta-programming macros Ruby allows you to construct classes using meta-programming macros like `acts_as_tree` or `has_many :items`. These macros will add methods, @@ -27,81 +30,83 @@ and macros. Here is an example of a `strip_field` macro, which created setter methods that remove leading and trailing whitespace from newly assigned values: - # app/models/article.rb - class Article < ActiveRecord::Base - include DoesStripFields[:name, :brand] - end - - # app/models/shared/does_strip_fields.rb - module DoesStripFields - as_trait do |*fields| - fields.each do |field| - define_method("#{field}=") do |value| - self[field] = value.strip - end - end +``` +# app/models/article.rb +class Article < ActiveRecord::Base + include DoesStripFields[:name, :brand] +end + +# app/models/shared/does_strip_fields.rb +module DoesStripFields + as_trait do |*fields| + fields.each do |field| + define_method("#{field}=") do |value| + self[field] = value.strip end end + end +end +``` Notice the `as_trait` block. We like to add `app/models/shared` and `app/controllers/shared` to the load paths of our Rails projects. These are great places to store macros that are re-used from multiple classes. - -Example 2: Mixins with class methods ------------------------------------- +## Example 2: Mixins with class methods 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). Modularity does away with the clutter and lets you say this: - # app/models/model.rb - class Model - include Mixin +``` +# app/models/model.rb +class Model + include Mixin +end + +# app/models/mixin.rb +module Mixin + as_trait do + def instance_method + # ... end - - # app/models/mixin.rb - module Mixin - as_trait do - def instance_method - # ... - end - def self.class_method - # .. - end - end + def self.class_method + # .. end + end +end +``` `private` and `protected` will also work as expected when defining a trait. - -Example 3: Splitting a model into multiple source files -------------------------------------------------------- +## Example 3: Splitting a model into multiple source files Models are often concerned with multiple themes like "authentication", "contact info" or "permissions", each requiring a couple of validations and callbacks here, and some method there. Modularity lets you organize your model into multiple partial classes, so each file can deal with a single aspect of your model: - # app/models/user.rb - class User < ActiveRecord::Base - include DoesAuthentication - include DoesPermissions - end - - # app/models/user/does_authentication.rb - module User::DoesAuthentication - as_trait do - # methods, validations, etc. regarding usernames and passwords go here - end - end - - # app/models/user/does_permissions.rb - module User::DoesPermissions - as_trait do - # methods, validations, etc. regarding contact information go here - end - end +``` +# app/models/user.rb +class User < ActiveRecord::Base + include DoesAuthentication + include DoesPermissions +end + +# app/models/user/does_authentication.rb +module User::DoesAuthentication + as_trait do + # methods, validations, etc. regarding usernames and passwords go here + end +end + +# app/models/user/does_permissions.rb +module User::DoesPermissions + as_trait do + # methods, validations, etc. regarding contact information go here + end +end +``` Some criticism has been raised for splitting large models into files like this. Essentially, even though have an easier time navigating your code, you will still @@ -110,41 +115,6 @@ have one giant model with many side effects. There are [many better ways](http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/) to decompose a huge Ruby class. - -Installation ------------- - -Add the following to your `Gemfile`: - - gem 'modularity', '>=2' - -Now run `bundle install`. - - -Migrating from Modularity 1 ---------------------------- - -If you have been using Modularity 1 with the `does` syntax, we provide a script to migrate your Ruby project -automatically. - -1. Make sure your project has tests and you have a backup of your files (or pushed your commits to Git) - -2. From your project directory, do this: - - find . -name "*.rb" | migrate-modularity1-to-modularity2 - -3. The script will rename your files and change your code. It will also syntax-check your files after conversion - (since the script is not perfect). - -4. Check the diff to see what the script has done. - -5. Run tests to see if everything still works. - - - - -Credits -------- +## Credits Henning Koch from [makandra.com](http://makandra.com/) - diff --git a/bin/migrate-modularity1-to-modularity2 b/bin/migrate-modularity1-to-modularity2 deleted file mode 100755 index 3bb149e..0000000 --- a/bin/migrate-modularity1-to-modularity2 +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__)) + '/../lib/modularity/migrator' - -ARGF.each do |path| - Modularity::Migrator.migrate(path.strip) -end diff --git a/lib/modularity/migrator.rb b/lib/modularity/migrator.rb deleted file mode 100644 index ed8733b..0000000 --- a/lib/modularity/migrator.rb +++ /dev/null @@ -1,53 +0,0 @@ -gem 'activesupport' -require 'active_support/all' -require 'fileutils' - -module Modularity - class Migrator - class << self - - def migrate(old_path) - old_path = File.expand_path(old_path) - new_path = fix_filename(old_path) - rename(old_path, new_path) unless old_path == new_path - old_code = File.read(new_path) - new_code = fix_code(old_code) - rewrite_file(new_path, new_code) unless old_code == new_code - puts "Migrated #{old_path}" - `ruby -c #{new_path}` - new_code - end - - private - - def fix_filename(path) - path = File.expand_path(path) - new_path = path.sub(/\/([^\/]+)_trait\.rb$/, '/does_\\1.rb') - new_path - end - - def fix_code(code) - code = code.gsub(/module (.*?)([A-Za-z0-9_]+)Trait\b/, 'module \\1Does\\2') - code = code.gsub(/does ['":]([A-Za-z0-9\_\/]+)(?:'|"|$)(?:,\s*(.*)$)?/) do - trait_path = $1 - parameters = $2 - trait_path = trait_path.sub(/([A-Za-z0-9\_]+)$/, 'does_\\1') - trait_class = trait_path.camelize # don't use classify, it removes plurals! - substituted = "include #{trait_class}" - substituted << "[#{parameters}]" if parameters - substituted - end - code - end - - def rename(old, new) - FileUtils.mv(old, new) - end - - def rewrite_file(path, content) - File.open(path, "w") { |file| file.write(content) } - end - - end - end -end diff --git a/spec/modularity/migrator_spec.rb b/spec/modularity/migrator_spec.rb deleted file mode 100644 index 05ce32a..0000000 --- a/spec/modularity/migrator_spec.rb +++ /dev/null @@ -1,109 +0,0 @@ -require 'spec_helper' - -require 'modularity/migrator' # this is an optional file and thus not loaded automatically - -describe Modularity::Migrator do - - subject do - Modularity::Migrator - end - - describe '.fix_filename' do - - it 'should replace the suffix _trait with a prefix does' do - subject.send(:fix_filename, '/path/to/search_trait.rb').should == '/path/to/does_search.rb' - end - - it "should not rename files that don't end in _trait.rb" do - subject.send(:fix_filename, '/path/to/search.rb').should == '/path/to/search.rb' - end - - end - - describe '.fix_code' do - - it 'renames a module FooBarTrait to DoesFooBar' do - - old_code = <<-RUBY - module Namespace::FooBarTrait - as_trait do - define_method :foo do - end - define_method :bar do - end - end - end - RUBY - - new_code = <<-RUBY - module Namespace::DoesFooBar - as_trait do - define_method :foo do - end - define_method :bar do - end - end - end - RUBY - - subject.send(:fix_code, old_code).should == new_code - end - - it "does not rename modules that aren't traits" do - - old_code = <<-RUBY - module Namespace::FooBar - def foo - end - def bar - end - end - RUBY - - subject.send(:fix_code, old_code).should == old_code - end - - it 'replaces does calls with include' do - - old_code = <<-RUBY - class User < ActiveRecord::Base - does 'user/search' - does 'user/account_settings' - does 'trashable' - end - RUBY - - new_code = <<-RUBY - class User < ActiveRecord::Base - include User::DoesSearch - include User::DoesAccountSettings - include DoesTrashable - end - RUBY - - subject.send(:fix_code, old_code).should == new_code - end - - it 'puts does parameters into square brackets' do - - old_code = <<-RUBY - class User < ActiveRecord::Base - does 'flag', :active, :default => true - does 'record/search', :field => :email - end - RUBY - - new_code = <<-RUBY - class User < ActiveRecord::Base - include DoesFlag[:active, :default => true] - include Record::DoesSearch[:field => :email] - end - RUBY - - subject.send(:fix_code, old_code).should == new_code - - end - - end - -end From f28357a5d6c4a23454154756377005d83477a81a Mon Sep 17 00:00:00 2001 From: Emanuel Denzel Date: Tue, 24 Aug 2021 15:04:24 +0200 Subject: [PATCH 16/32] Upgrade to RSpec 3 --- .gitignore | 1 - .rspec | 1 + CHANGELOG.md | 3 ++- Gemfile | 4 +++ Gemfile.lock | 45 ++++++++++++++++++++++++++++++++ Rakefile | 14 ++++------ bin/console | 9 +++++++ modularity.gemspec | 5 ---- spec/modularity/as_trait_spec.rb | 2 -- spec/rcov.opts | 2 -- spec/spec.opts | 4 --- spec/spec_helper.rb | 8 +++--- 12 files changed, 71 insertions(+), 27 deletions(-) create mode 100644 .rspec create mode 100644 Gemfile.lock create mode 100755 bin/console delete mode 100755 spec/rcov.opts delete mode 100755 spec/spec.opts diff --git a/.gitignore b/.gitignore index 6d88d9c..8c0f972 100755 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ doc pkg *.gem .idea -Gemfile.lock diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..c99d2e7 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/CHANGELOG.md b/CHANGELOG.md index 5638170..5e84cd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,4 +10,5 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html - Removed migration guide for modularity version 1. ### Compatible changes -- This CHANGELOG file. + +- Added this CHANGELOG file. diff --git a/Gemfile b/Gemfile index c80ee36..0c50bee 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,7 @@ source "http://rubygems.org" gemspec + +gem 'rake' +gem 'rspec' +gem 'pry-byebug' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..811353e --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,45 @@ +PATH + remote: . + specs: + modularity (2.0.1) + +GEM + remote: http://rubygems.org/ + specs: + byebug (11.1.3) + coderay (1.1.3) + diff-lcs (1.4.4) + method_source (1.0.0) + pry (0.13.1) + coderay (~> 1.1) + method_source (~> 1.0) + pry-byebug (3.9.0) + byebug (~> 11.0) + pry (~> 0.13.0) + rake (13.0.6) + rspec (3.10.0) + rspec-core (~> 3.10.0) + rspec-expectations (~> 3.10.0) + rspec-mocks (~> 3.10.0) + rspec-core (3.10.1) + rspec-support (~> 3.10.0) + rspec-expectations (3.10.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-mocks (3.10.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-support (3.10.2) + +PLATFORMS + ruby + x86_64-linux + +DEPENDENCIES + modularity! + pry-byebug + rake + rspec + +BUNDLED WITH + 1.17.3 diff --git a/Rakefile b/Rakefile index 8c158fe..b1f046f 100755 --- a/Rakefile +++ b/Rakefile @@ -1,12 +1,8 @@ -require 'rake' -require 'spec/rake/spectask' require 'bundler/gem_tasks' +require 'rspec/core/rake_task' -desc 'Default: Run all specs.' -task :default => :spec - -desc "Run all specs" -Spec::Rake::SpecTask.new() do |t| - t.spec_opts = ['--options', "\"spec/spec.opts\""] - t.spec_files = FileList['spec/**/*_spec.rb'] +RSpec::Core::RakeTask.new(:spec) do |task| + task.verbose = false end + +task default: :spec diff --git a/bin/console b/bin/console new file mode 100755 index 0000000..5f3a90a --- /dev/null +++ b/bin/console @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby + +require 'bundler/setup' +require 'modularity' +require 'pry' + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. +Pry.start diff --git a/modularity.gemspec b/modularity.gemspec index f479b00..1064825 100755 --- a/modularity.gemspec +++ b/modularity.gemspec @@ -16,9 +16,4 @@ Gem::Specification.new do |s| s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] - - s.add_development_dependency('rake') - s.add_development_dependency('rspec', '<2') - s.add_development_dependency('rspec_candy') - end diff --git a/spec/modularity/as_trait_spec.rb b/spec/modularity/as_trait_spec.rb index 37917e3..1d57788 100755 --- a/spec/modularity/as_trait_spec.rb +++ b/spec/modularity/as_trait_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' - describe Modularity::AsTrait do describe '.included' do diff --git a/spec/rcov.opts b/spec/rcov.opts deleted file mode 100755 index 274ed51..0000000 --- a/spec/rcov.opts +++ /dev/null @@ -1,2 +0,0 @@ ---exclude "spec/*,gems/*" ---rails \ No newline at end of file diff --git a/spec/spec.opts b/spec/spec.opts deleted file mode 100755 index 391705b..0000000 --- a/spec/spec.opts +++ /dev/null @@ -1,4 +0,0 @@ ---colour ---format progress ---loadby mtime ---reverse diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 85aa730..b86b309 100755 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,6 @@ -$: << File.join(File.dirname(__FILE__), "/../lib" ) +require 'modularity' -require "#{File.dirname(__FILE__)}/../lib/modularity" -require 'rspec_candy/all' +RSpec.configure do |config| + config.expect_with(:rspec) { |expects| expects.syntax = :should } + config.mock_with(:rspec) { |mocks| mocks.syntax = [:should, :receive] } +end From f377908001bea0d8c23c43a53dd4f9c8cea82724 Mon Sep 17 00:00:00 2001 From: Emanuel Denzel Date: Tue, 24 Aug 2021 15:12:40 +0200 Subject: [PATCH 17/32] Update rubygems config and github defaults --- LICENSE.txt | 21 +++++++++++++++++++++ MIT-LICENSE | 20 -------------------- modularity.gemspec | 38 +++++++++++++++++++++++--------------- 3 files changed, 44 insertions(+), 35 deletions(-) create mode 100644 LICENSE.txt delete mode 100755 MIT-LICENSE diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..646e18a --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2009 Henning Koch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/MIT-LICENSE b/MIT-LICENSE deleted file mode 100755 index 30adc46..0000000 --- a/MIT-LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2009 Henning Koch - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/modularity.gemspec b/modularity.gemspec index 1064825..8ff303b 100755 --- a/modularity.gemspec +++ b/modularity.gemspec @@ -1,19 +1,27 @@ -# -*- encoding: utf-8 -*- -$:.push File.expand_path("../lib", __FILE__) +lib = File.expand_path('lib', __dir__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'modularity/version' -Gem::Specification.new do |s| - s.name = %q{modularity} - s.version = Modularity::VERSION - s.licenses = ["MIT"] - s.authors = ["Henning Koch"] - s.email = %q{github@makandra.de} - s.homepage = %q{http://github.com/makandra/modularity} - s.summary = %q{Traits and partial classes for Ruby} - s.description = %q{Traits and partial classes for Ruby} +Gem::Specification.new do |spec| + spec.name = 'modularity' + spec.version = Modularity::VERSION + spec.required_ruby_version = '>= 2.5.0' + spec.authors = ['Henning Koch'] + spec.email = ['henning.koch@makandra.de'] - s.files = `git ls-files`.split("\n") - s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") - s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } - s.require_paths = ["lib"] + spec.summary = 'Traits and partial classes for Ruby' + spec.description = 'Traits and partial classes for Ruby' + spec.homepage = 'https://github.com/makandra/modularity' + spec.license = 'MIT' + + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + spec.files = Dir.chdir(File.expand_path(__dir__)) do + `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + end + spec.bindir = 'exe' + spec.executables = spec.files.grep(%r(^exe/)) { |f| File.basename(f) } + spec.require_paths = ['lib'] + + # Development dependencies are defined in the Gemfile (therefore no `spec.add_development_dependency` directives) end From 452b76a608b007b8bac9432cb8a65d1aa013a32b Mon Sep 17 00:00:00 2001 From: Emanuel Denzel Date: Tue, 24 Aug 2021 15:55:06 +0200 Subject: [PATCH 18/32] Add gemika and Github actions for testing --- .github/workflows/test.yml | 36 ++++++++++++++++++++++++++++++++++++ .ruby-version | 1 + CHANGELOG.md | 1 + Gemfile | 3 ++- Gemfile.lock | 4 +++- README.md | 8 ++++++++ Rakefile | 9 +++------ 7 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 .ruby-version diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..d5d36b2 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,36 @@ +--- +name: Tests +'on': + push: + branches: + - master + pull_request: + branches: + - master +jobs: + test: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + include: + - ruby: 2.5.7 + gemfile: Gemfile + - ruby: 2.7.4 + gemfile: Gemfile + - ruby: 3.0.2 + gemfile: Gemfile + env: + BUNDLE_GEMFILE: "${{ matrix.gemfile }}" + steps: + - uses: actions/checkout@v2 + - name: Install ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "${{ matrix.ruby }}" + - name: Bundle + run: | + gem install bundler:2.2.22 + bundle install --no-deployment + - name: Run tests + run: bundle exec rspec diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..35d16fb --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.5.7 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e84cd8..3be3d3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html ### Breaking changes - Removed migration guide for modularity version 1. +- Removed support for Ruby < `2.5.0`. ### Compatible changes diff --git a/Gemfile b/Gemfile index 0c50bee..f0d494f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,8 @@ -source "http://rubygems.org" +source 'http://rubygems.org' gemspec gem 'rake' gem 'rspec' gem 'pry-byebug' +gem 'gemika' diff --git a/Gemfile.lock b/Gemfile.lock index 811353e..b9be766 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,6 +9,7 @@ GEM byebug (11.1.3) coderay (1.1.3) diff-lcs (1.4.4) + gemika (0.6.1) method_source (1.0.0) pry (0.13.1) coderay (~> 1.1) @@ -36,10 +37,11 @@ PLATFORMS x86_64-linux DEPENDENCIES + gemika modularity! pry-byebug rake rspec BUNDLED WITH - 1.17.3 + 2.2.22 diff --git a/README.md b/README.md index 43353d7..6953e73 100755 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Tests](https://github.com/makandra/modularity/workflows/Tests/badge.svg)](https://github.com/makandra/modularity/actions) + # Modularity 2 - Traits and partial classes for Ruby Modularity enhances Ruby's [`Module`](http://apidock.com/ruby/Module) so it can be used traits and partial classes. @@ -115,6 +117,12 @@ have one giant model with many side effects. There are [many better ways](http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/) to decompose a huge Ruby class. +## Development + +* Install Bundler 2 `gem install bundler:2.2.22` and run `bundle install` to have a working development setup. +* Running tests for the current Ruby version: `bundle exec rake` +* 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 + ## Credits Henning Koch from [makandra.com](http://makandra.com/) diff --git a/Rakefile b/Rakefile index b1f046f..666a2e5 100755 --- a/Rakefile +++ b/Rakefile @@ -1,8 +1,5 @@ require 'bundler/gem_tasks' -require 'rspec/core/rake_task' +require 'bundler/setup' +require 'gemika/tasks' -RSpec::Core::RakeTask.new(:spec) do |task| - task.verbose = false -end - -task default: :spec +task default: 'matrix:spec' From be12d70bd9026210a4da16b8395cbc8fd6180558 Mon Sep 17 00:00:00 2001 From: Emanuel Denzel Date: Tue, 24 Aug 2021 16:05:14 +0200 Subject: [PATCH 19/32] Bump version from 2.0.1 to 3.0.0 --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 4 ++-- lib/modularity/version.rb | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3be3d3c..18d8100 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html ### Breaking changes +### Compatible changes + + +## 3.0.0 - 2021-08-24 + +### Breaking changes + - Removed migration guide for modularity version 1. - Removed support for Ruby < `2.5.0`. diff --git a/Gemfile.lock b/Gemfile.lock index b9be766..8720fcd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - modularity (2.0.1) + modularity (3.0.0) GEM remote: http://rubygems.org/ @@ -44,4 +44,4 @@ DEPENDENCIES rspec BUNDLED WITH - 2.2.22 + 2.2.26 diff --git a/lib/modularity/version.rb b/lib/modularity/version.rb index e8322d8..7b39506 100644 --- a/lib/modularity/version.rb +++ b/lib/modularity/version.rb @@ -1,3 +1,3 @@ module Modularity - VERSION = '2.0.1' + VERSION = '3.0.0' end From 3c19c84363a5d9f499eae4bf7f49427a919bdb46 Mon Sep 17 00:00:00 2001 From: Emanuel Denzel Date: Tue, 24 Aug 2021 16:07:33 +0200 Subject: [PATCH 20/32] Remove version from title --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6953e73..683645c 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Tests](https://github.com/makandra/modularity/workflows/Tests/badge.svg)](https://github.com/makandra/modularity/actions) -# Modularity 2 - Traits and partial classes for Ruby +# Modularity - Traits and partial classes for Ruby Modularity enhances Ruby's [`Module`](http://apidock.com/ruby/Module) so it can be used traits and partial classes. This allows very simple definition of meta-programming macros like the From 20373fc7c41b65f1a1029cab75cfe0b687adffd2 Mon Sep 17 00:00:00 2001 From: Niklas Hasselmeyer Date: Thu, 10 Feb 2022 16:41:13 +0100 Subject: [PATCH 21/32] Activate rubygems MFA --- modularity.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/modularity.gemspec b/modularity.gemspec index 8ff303b..77d626f 100755 --- a/modularity.gemspec +++ b/modularity.gemspec @@ -13,6 +13,7 @@ Gem::Specification.new do |spec| spec.description = 'Traits and partial classes for Ruby' spec.homepage = 'https://github.com/makandra/modularity' spec.license = 'MIT' + spec.metadata = { 'rubygems_mfa_required' => 'true' } # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. From 17b72537c6ec84a11eacf5eec6abe0b0d3b48edb Mon Sep 17 00:00:00 2001 From: Bruno Sedler Date: Wed, 9 Mar 2022 16:37:43 +0100 Subject: [PATCH 22/32] Bump version from 3.0.0 to 3.0.1 --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 4 ++-- lib/modularity/version.rb | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18d8100..7f59ef8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,13 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html ### Compatible changes +## 3.0.1 - 2022-03-09 + +### Compatible changes + +- Activate Rubygems MFA + + ## 3.0.0 - 2021-08-24 ### Breaking changes diff --git a/Gemfile.lock b/Gemfile.lock index 8720fcd..68730eb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - modularity (3.0.0) + modularity (3.0.1) GEM remote: http://rubygems.org/ @@ -44,4 +44,4 @@ DEPENDENCIES rspec BUNDLED WITH - 2.2.26 + 2.2.32 diff --git a/lib/modularity/version.rb b/lib/modularity/version.rb index 7b39506..9ca6d7e 100644 --- a/lib/modularity/version.rb +++ b/lib/modularity/version.rb @@ -1,3 +1,3 @@ module Modularity - VERSION = '3.0.0' + VERSION = '3.0.1' end From cf62df69267b6b63457ad2c001ab77c8aadd7272 Mon Sep 17 00:00:00 2001 From: Bruno Sedler Date: Fri, 20 May 2022 14:55:53 +0200 Subject: [PATCH 23/32] Add support for separation of positional and keyword arguments in ruby 3 --- .github/workflows/test.yml | 4 ++++ lib/modularity/as_trait.rb | 22 +++++++++++++++++----- spec/modularity/as_trait_spec.rb | 29 +++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d5d36b2..34b602b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,10 +16,14 @@ jobs: include: - ruby: 2.5.7 gemfile: Gemfile + - ruby: 2.6.10 + gemfile: Gemfile - ruby: 2.7.4 gemfile: Gemfile - ruby: 3.0.2 gemfile: Gemfile + - ruby: 3.1.2 + gemfile: Gemfile env: BUNDLE_GEMFILE: "${{ matrix.gemfile }}" steps: diff --git a/lib/modularity/as_trait.rb b/lib/modularity/as_trait.rb index f1145bd..bbc3f0a 100755 --- a/lib/modularity/as_trait.rb +++ b/lib/modularity/as_trait.rb @@ -2,14 +2,19 @@ module Modularity class ParametrizedTrait < Module - def initialize(blank_trait, args) + def initialize(blank_trait, args, kwargs) @args = args + @kwargs = kwargs @macro = blank_trait.instance_variable_get(:@modularity_macro) include(blank_trait) end def included(base) - base.class_exec(*@args, &@macro) + if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7') + base.class_exec(*@args, &@macro) + else + base.class_exec(*@args, **@kwargs, &@macro) + end end end @@ -27,9 +32,16 @@ def self.included(base) end - def self.[](*args) - blank_trait = self - ParametrizedTrait.new(blank_trait, args) + if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7') + def self.[](*args) + blank_trait = self + ParametrizedTrait.new(blank_trait, args, {}) + end + else + def self.[](*args, **kwargs) + blank_trait = self + ParametrizedTrait.new(blank_trait, args, kwargs) + end end end diff --git a/spec/modularity/as_trait_spec.rb b/spec/modularity/as_trait_spec.rb index 1d57788..d0cca6a 100755 --- a/spec/modularity/as_trait_spec.rb +++ b/spec/modularity/as_trait_spec.rb @@ -201,6 +201,35 @@ def vanilla_method end + it 'passes keyword args to the block given to as_trait' do + + module ModuleWithKeywordArgs + as_trait do |hash, required_kwarg:, optional_kwarg: 'foo'| + define_method :passed_hash do + hash + end + + define_method :required_keyword do + required_kwarg + end + + define_method :optional_keyword do + optional_kwarg + end + + end + end + + @doing_class.class_eval do + include ModuleWithKeywordArgs[{ first_hash_key: 'value_one', second_hash_key: 'value_two' }, required_kwarg: 'bar'] + end + + instance = @doing_class.new + instance.passed_hash.should eq({ first_hash_key: 'value_one', second_hash_key: 'value_two' }) + instance.required_keyword.should eq('bar') + instance.optional_keyword.should eq('foo') + end + end end From 8fe03e0291988f63cca53e1f12849ea655e508d4 Mon Sep 17 00:00:00 2001 From: Bruno Sedler Date: Wed, 1 Jun 2022 17:58:53 +0200 Subject: [PATCH 24/32] Bump version from 3.0.1 to 3.1.0 --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/modularity/version.rb | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f59ef8..8a49c59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,13 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html ### Compatible changes +## 3.1.0 - 2022-06-01 + +### Compatible changes + +- Add support for separation of positional and keyword arguments in ruby 3 + + ## 3.0.1 - 2022-03-09 ### Compatible changes diff --git a/Gemfile.lock b/Gemfile.lock index 68730eb..b2cbd89 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - modularity (3.0.1) + modularity (3.1.0) GEM remote: http://rubygems.org/ diff --git a/lib/modularity/version.rb b/lib/modularity/version.rb index 9ca6d7e..7fcb430 100644 --- a/lib/modularity/version.rb +++ b/lib/modularity/version.rb @@ -1,3 +1,3 @@ module Modularity - VERSION = '3.0.1' + VERSION = '3.1.0' end From 2bfdf44e0df18383d984ff0d34f4d3ff1f312f6e Mon Sep 17 00:00:00 2001 From: Henning Koch Date: Wed, 5 Oct 2022 13:11:15 +0200 Subject: [PATCH 25/32] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 683645c..8937d56 100755 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ in a way that is less awkward than using modules. Add the following to your `Gemfile`: -``` +```ruby gem 'modularity' ``` @@ -32,7 +32,7 @@ and macros. Here is an example of a `strip_field` macro, which created setter methods that remove leading and trailing whitespace from newly assigned values: -``` +```ruby # app/models/article.rb class Article < ActiveRecord::Base include DoesStripFields[:name, :brand] @@ -61,7 +61,7 @@ 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). Modularity does away with the clutter and lets you say this: -``` +```ruby # app/models/model.rb class Model include Mixin @@ -88,7 +88,7 @@ Models are often concerned with multiple themes like "authentication", "contact a couple of validations and callbacks here, and some method there. Modularity lets you organize your model into multiple partial classes, so each file can deal with a single aspect of your model: -``` +```ruby # app/models/user.rb class User < ActiveRecord::Base include DoesAuthentication From f3e8972c3b141a32c4eace5f36d4d2f6e1501b84 Mon Sep 17 00:00:00 2001 From: Bruno Sedler Date: Wed, 22 Feb 2023 09:56:09 +0100 Subject: [PATCH 26/32] Add support for Ruby 3.2 --- .github/workflows/test.yml | 2 ++ CHANGELOG.md | 2 ++ Gemfile.lock | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 34b602b..79e2a05 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,6 +24,8 @@ jobs: gemfile: Gemfile - ruby: 3.1.2 gemfile: Gemfile + - ruby: 3.2.1 + gemfile: Gemfile env: BUNDLE_GEMFILE: "${{ matrix.gemfile }}" steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a49c59..0e4e865 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html ### Compatible changes +- Add support for Ruby 3.2 + ## 3.1.0 - 2022-06-01 diff --git a/Gemfile.lock b/Gemfile.lock index b2cbd89..fd170f6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,7 +9,7 @@ GEM byebug (11.1.3) coderay (1.1.3) diff-lcs (1.4.4) - gemika (0.6.1) + gemika (0.8.1) method_source (1.0.0) pry (0.13.1) coderay (~> 1.1) From 9d08cf37ad4d1454533e539a41b62aba06842313 Mon Sep 17 00:00:00 2001 From: Bruno Sedler Date: Wed, 1 Mar 2023 15:34:28 +0100 Subject: [PATCH 27/32] Bump version from 3.1.0 to 3.2.0 --- CHANGELOG.md | 4 ++++ Gemfile.lock | 2 +- lib/modularity/version.rb | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e4e865..cfc0ab3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html ### Compatible changes +## 3.2.0 - 2023-03-01 + +### Compatible changes + - Add support for Ruby 3.2 diff --git a/Gemfile.lock b/Gemfile.lock index fd170f6..457c50d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - modularity (3.1.0) + modularity (3.2.0) GEM remote: http://rubygems.org/ diff --git a/lib/modularity/version.rb b/lib/modularity/version.rb index 7fcb430..d40bd34 100644 --- a/lib/modularity/version.rb +++ b/lib/modularity/version.rb @@ -1,3 +1,3 @@ module Modularity - VERSION = '3.1.0' + VERSION = '3.2.0' end From 5fa550c46a3ec5a90368a3a8748faa6a1bd93890 Mon Sep 17 00:00:00 2001 From: Dennis Schreiner Date: Fri, 6 Dec 2024 13:15:06 +0100 Subject: [PATCH 28/32] Add support for Ruby 3.3 --- .github/workflows/test.yml | 2 ++ CHANGELOG.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 79e2a05..f39a97e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,6 +26,8 @@ jobs: gemfile: Gemfile - ruby: 3.2.1 gemfile: Gemfile + - ruby: 3.3.6 + gemfile: Gemfile env: BUNDLE_GEMFILE: "${{ matrix.gemfile }}" steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index cfc0ab3..c4a6ad8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html ### Compatible changes +- Add support for Ruby 3.3 + ## 3.2.0 - 2023-03-01 ### Compatible changes From 04b4abf48f9d2e2a414681683f2229935b00af08 Mon Sep 17 00:00:00 2001 From: Dennis Schreiner Date: Fri, 14 Feb 2025 15:45:11 +0100 Subject: [PATCH 29/32] Add support for Ruby 3.4 --- .github/workflows/test.yml | 4 +++- CHANGELOG.md | 1 + Gemfile.lock | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f39a97e..ca4ed8f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,8 @@ jobs: gemfile: Gemfile - ruby: 3.3.6 gemfile: Gemfile + - ruby: 3.4.1 + gemfile: Gemfile env: BUNDLE_GEMFILE: "${{ matrix.gemfile }}" steps: @@ -38,7 +40,7 @@ jobs: ruby-version: "${{ matrix.ruby }}" - name: Bundle run: | - gem install bundler:2.2.22 + gem install bundler:2.3.27 bundle install --no-deployment - name: Run tests run: bundle exec rspec diff --git a/CHANGELOG.md b/CHANGELOG.md index c4a6ad8..908d6e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html ### Compatible changes - Add support for Ruby 3.3 +- Add support for Ruby 3.4 ## 3.2.0 - 2023-03-01 diff --git a/Gemfile.lock b/Gemfile.lock index 457c50d..28e2709 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -44,4 +44,4 @@ DEPENDENCIES rspec BUNDLED WITH - 2.2.32 + 2.3.27 From 7117cba238a3914be9a50eea55220728806d0ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=B6ler?= Date: Thu, 15 May 2025 11:52:23 +0200 Subject: [PATCH 30/32] Update Github Actions runner image --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ca4ed8f..ffce818 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ name: Tests - master jobs: test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: From bb5939f8aac5e2be2f6cca70994c3dd8edb3e9c3 Mon Sep 17 00:00:00 2001 From: Dennis Schreiner Date: Thu, 29 Jan 2026 09:04:03 +0100 Subject: [PATCH 31/32] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8937d56..af408b4 100755 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ Some criticism has been raised for splitting large models into files like this. Essentially, even though have an easier time navigating your code, you will still have one giant model with many side effects. -There are [many better ways](http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/) +There are [many better ways](https://codeclimate.com/blog/7-ways-to-decompose-fat-activerecord-models) to decompose a huge Ruby class. ## Development From cd835455e23cac3090812c54a30fb4d095c4d42d Mon Sep 17 00:00:00 2001 From: Dennis Schreiner <99013339+tecden@users.noreply.github.com> Date: Tue, 24 Feb 2026 10:28:29 +0100 Subject: [PATCH 32/32] Add support for Ruby 4 --- .github/workflows/test.yml | 5 +++++ CHANGELOG.md | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ffce818..7dbb1f8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,7 @@ jobs: fail-fast: false matrix: include: + # Legacy - If a version breaks, just drop it, don't fix it. - ruby: 2.5.7 gemfile: Gemfile - ruby: 2.6.10 @@ -24,12 +25,16 @@ jobs: gemfile: Gemfile - ruby: 3.1.2 gemfile: Gemfile + + # Officially supported - ruby: 3.2.1 gemfile: Gemfile - ruby: 3.3.6 gemfile: Gemfile - ruby: 3.4.1 gemfile: Gemfile + - ruby: 4.0.1 + gemfile: Gemfile env: BUNDLE_GEMFILE: "${{ matrix.gemfile }}" steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 908d6e4..44da3fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,9 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html ### Breaking changes ### Compatible changes - - Add support for Ruby 3.3 - Add support for Ruby 3.4 +- Add support for Ruby 4.0 ## 3.2.0 - 2023-03-01