active_service 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/.document +3 -0
  2. data/.gitignore +7 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGELOG.rdoc +3 -0
  5. data/Gemfile +3 -0
  6. data/Gemfile.lock +101 -0
  7. data/README.rdoc +82 -0
  8. data/Rakefile +31 -0
  9. data/TODO.rdoc +4 -0
  10. data/active_service.gemspec +29 -0
  11. data/config/.gitkeep +0 -0
  12. data/db/database.yml +2 -0
  13. data/db/migrate/001_create_users.rb +11 -0
  14. data/lib/active_service.rb +17 -0
  15. data/lib/active_service/action_controller_extension.rb +26 -0
  16. data/lib/active_service/base.rb +17 -0
  17. data/lib/active_service/configuration.rb +20 -0
  18. data/lib/active_service/core_ext/action_controller/base.rb +6 -0
  19. data/lib/active_service/version.rb +3 -0
  20. data/lib/generators/active_service/config_generator.rb +16 -0
  21. data/lib/generators/active_service/service_generator.rb +18 -0
  22. data/spec/active_service_spec.rb +84 -0
  23. data/spec/configuration_spec.rb +24 -0
  24. data/spec/spec_helper.rb +36 -0
  25. data/spec/user.rb +4 -0
  26. data/test_app/.gitignore +4 -0
  27. data/test_app/Gemfile +12 -0
  28. data/test_app/Gemfile.lock +97 -0
  29. data/test_app/README +256 -0
  30. data/test_app/Rakefile +7 -0
  31. data/test_app/app/controllers/application_controller.rb +3 -0
  32. data/test_app/app/controllers/payments_controller.rb +9 -0
  33. data/test_app/app/helpers/application_helper.rb +2 -0
  34. data/test_app/app/helpers/payments_helper.rb +2 -0
  35. data/test_app/app/models/payment.rb +2 -0
  36. data/test_app/app/views/layouts/application.html.erb +14 -0
  37. data/test_app/config.ru +4 -0
  38. data/test_app/config/active_service.yml +1 -0
  39. data/test_app/config/application.rb +42 -0
  40. data/test_app/config/boot.rb +6 -0
  41. data/test_app/config/database.yml +22 -0
  42. data/test_app/config/environment.rb +5 -0
  43. data/test_app/config/environments/development.rb +26 -0
  44. data/test_app/config/environments/production.rb +49 -0
  45. data/test_app/config/environments/test.rb +35 -0
  46. data/test_app/config/initializers/backtrace_silencers.rb +7 -0
  47. data/test_app/config/initializers/inflections.rb +10 -0
  48. data/test_app/config/initializers/mime_types.rb +5 -0
  49. data/test_app/config/initializers/secret_token.rb +7 -0
  50. data/test_app/config/initializers/session_store.rb +8 -0
  51. data/test_app/config/locales/en.yml +5 -0
  52. data/test_app/config/routes.rb +60 -0
  53. data/test_app/db/migrate/20110531121211_create_payments.rb +15 -0
  54. data/test_app/db/schema.rb +23 -0
  55. data/test_app/db/seeds.rb +7 -0
  56. data/test_app/doc/README_FOR_APP +2 -0
  57. data/test_app/lib/services/payment_service.rb +5 -0
  58. data/test_app/lib/tasks/.gitkeep +0 -0
  59. data/test_app/public/404.html +26 -0
  60. data/test_app/public/422.html +26 -0
  61. data/test_app/public/500.html +26 -0
  62. data/test_app/public/favicon.ico +0 -0
  63. data/test_app/public/images/rails.png +0 -0
  64. data/test_app/public/javascripts/application.js +2 -0
  65. data/test_app/public/javascripts/controls.js +965 -0
  66. data/test_app/public/javascripts/dragdrop.js +974 -0
  67. data/test_app/public/javascripts/effects.js +1123 -0
  68. data/test_app/public/javascripts/prototype.js +6001 -0
  69. data/test_app/public/javascripts/rails.js +191 -0
  70. data/test_app/public/robots.txt +5 -0
  71. data/test_app/public/stylesheets/.gitkeep +0 -0
  72. data/test_app/script/rails +6 -0
  73. data/test_app/test/fixtures/payments.yml +11 -0
  74. data/test_app/test/functional/payments_controller_test.rb +8 -0
  75. data/test_app/test/performance/browsing_test.rb +9 -0
  76. data/test_app/test/test_helper.rb +13 -0
  77. data/test_app/test/unit/helpers/payments_helper_test.rb +4 -0
  78. data/test_app/test/unit/payment_test.rb +8 -0
  79. data/test_app/vendor/plugins/.gitkeep +0 -0
  80. metadata +197 -0
@@ -0,0 +1,3 @@
1
+ lib/**/*.rb
2
+ README.rdoc
3
+ CHANGELOG.rdoc
@@ -0,0 +1,7 @@
1
+ html
2
+ pkg
3
+ *.gem
4
+ .DS_Store
5
+ .bundle
6
+ db/*.sqlite3
7
+ db/*.log
@@ -0,0 +1,4 @@
1
+ script: "bundle exec rake"
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
@@ -0,0 +1,3 @@
1
+ == 0.0.1
2
+
3
+ * Documentation Driven Development
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,101 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ active_service (0.0.1)
5
+ rails (~> 3.0.0)
6
+ rake (~> 0.8.7)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ abstract (1.0.0)
12
+ actionmailer (3.0.7)
13
+ actionpack (= 3.0.7)
14
+ mail (~> 2.2.15)
15
+ actionpack (3.0.7)
16
+ activemodel (= 3.0.7)
17
+ activesupport (= 3.0.7)
18
+ builder (~> 2.1.2)
19
+ erubis (~> 2.6.6)
20
+ i18n (~> 0.5.0)
21
+ rack (~> 1.2.1)
22
+ rack-mount (~> 0.6.14)
23
+ rack-test (~> 0.5.7)
24
+ tzinfo (~> 0.3.23)
25
+ activemodel (3.0.7)
26
+ activesupport (= 3.0.7)
27
+ builder (~> 2.1.2)
28
+ i18n (~> 0.5.0)
29
+ activerecord (3.0.7)
30
+ activemodel (= 3.0.7)
31
+ activesupport (= 3.0.7)
32
+ arel (~> 2.0.2)
33
+ tzinfo (~> 0.3.23)
34
+ activeresource (3.0.7)
35
+ activemodel (= 3.0.7)
36
+ activesupport (= 3.0.7)
37
+ activesupport (3.0.7)
38
+ arel (2.0.10)
39
+ builder (2.1.2)
40
+ diff-lcs (1.1.2)
41
+ erubis (2.6.6)
42
+ abstract (>= 1.0.0)
43
+ fuubar (0.0.5)
44
+ rspec (~> 2.0)
45
+ rspec-instafail (~> 0.1.4)
46
+ ruby-progressbar (~> 0.0.10)
47
+ i18n (0.5.0)
48
+ mail (2.2.19)
49
+ activesupport (>= 2.3.6)
50
+ i18n (>= 0.4.0)
51
+ mime-types (~> 1.16)
52
+ treetop (~> 1.4.8)
53
+ mime-types (1.16)
54
+ polyglot (0.3.1)
55
+ rack (1.2.3)
56
+ rack-mount (0.6.14)
57
+ rack (>= 1.0.0)
58
+ rack-test (0.5.7)
59
+ rack (>= 1.0)
60
+ rails (3.0.7)
61
+ actionmailer (= 3.0.7)
62
+ actionpack (= 3.0.7)
63
+ activerecord (= 3.0.7)
64
+ activeresource (= 3.0.7)
65
+ activesupport (= 3.0.7)
66
+ bundler (~> 1.0)
67
+ railties (= 3.0.7)
68
+ railties (3.0.7)
69
+ actionpack (= 3.0.7)
70
+ activesupport (= 3.0.7)
71
+ rake (>= 0.8.7)
72
+ thor (~> 0.14.4)
73
+ rake (0.8.7)
74
+ rdoc (3.6.1)
75
+ rspec (2.6.0)
76
+ rspec-core (~> 2.6.0)
77
+ rspec-expectations (~> 2.6.0)
78
+ rspec-mocks (~> 2.6.0)
79
+ rspec-core (2.6.3)
80
+ rspec-expectations (2.6.0)
81
+ diff-lcs (~> 1.1.2)
82
+ rspec-instafail (0.1.7)
83
+ rspec-mocks (2.6.0)
84
+ ruby-progressbar (0.0.10)
85
+ sqlite3 (1.3.3)
86
+ sqlite3-ruby (1.3.3)
87
+ sqlite3 (>= 1.3.3)
88
+ thor (0.14.6)
89
+ treetop (1.4.9)
90
+ polyglot (>= 0.3.1)
91
+ tzinfo (0.3.27)
92
+
93
+ PLATFORMS
94
+ ruby
95
+
96
+ DEPENDENCIES
97
+ active_service!
98
+ fuubar (~> 0.0.4)
99
+ rdoc (~> 3.6.1)
100
+ rspec (~> 2.6.0)
101
+ sqlite3-ruby (~> 1.3.3)
@@ -0,0 +1,82 @@
1
+ Status: http://stillmaintained.com/phoet/active_service.png http://travis-ci.org/phoet/active_service.png
2
+
3
+ ActiveService is a Rails extension that aims at
4
+ creating reusable, transactional services in
5
+ your application to make your code more DRY.
6
+
7
+ == Installation
8
+
9
+ With bundler:
10
+
11
+ # Gemfile
12
+ gem 'active_service'
13
+
14
+ $ bundle install
15
+
16
+ or directly
17
+
18
+ $ (sudo) gem install active_service
19
+
20
+ == Usage
21
+
22
+ Start by generating a service:
23
+
24
+ $ rails generate active_service:service payment
25
+
26
+ This will generate an empty stub for you in _app/services_:
27
+
28
+ # app/services/payment_service.rb
29
+ class PaymentService < ActiveService::Base
30
+ def do_something_important
31
+ # your important code that should not fuck up comes here
32
+ end
33
+ end
34
+
35
+ If you want to use this service in your controller, just add a makro for it:
36
+
37
+ # app/controllers/
38
+ class PaymentController < ApplicationController
39
+ service :payment
40
+
41
+ def crazy_payment_action
42
+ if services(:payment).do_something_important
43
+ flash.info 'epic win!'
44
+ else
45
+ redirect_to :back, :error => 'epic fail!'
46
+ end
47
+ end
48
+ end
49
+
50
+ This makro is also available in the services themselves, so that you can nest them.
51
+
52
+ == Good to know
53
+
54
+ You can always disable the transactional behavior, which might be useful when nesting services,
55
+ by using the bang-style method name:
56
+
57
+ service.do_something_important!
58
+
59
+ ActiveService will wrap all the methods that you define in your service.
60
+ If you want to execute them directly you can do this by prefixing them:
61
+
62
+ service.__do_something_important
63
+
64
+ Nested Transactions are NOT supported!
65
+
66
+ == Configuration
67
+
68
+ $ rails generate active_service:config
69
+
70
+ After running the Rails generator you have a new folder _app/services_
71
+ where you can place your services. This folder is added to the rails load-path automagically.
72
+
73
+ If you do not like this folder have a look at the default configuration which has been
74
+ generated into an initializer at _config/active_service.yml_:
75
+
76
+ path: "lib/services"
77
+
78
+ == More on Transactions
79
+
80
+ Have a look into the API-Docs[http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html]
81
+ for more information about Transactions.
82
+
@@ -0,0 +1,31 @@
1
+ require "bundler"
2
+ require "rdoc/task"
3
+ require "rspec/core/rake_task"
4
+ require 'active_record'
5
+ require 'yaml'
6
+ require 'logger'
7
+
8
+ Bundler::GemHelper.install_tasks
9
+
10
+ desc "Migrate the database through scripts in db/migrate. Target specific version with VERSION=x"
11
+ task :migrate => :environment do
12
+ ActiveRecord::Migration.verbose = true
13
+ ActiveRecord::Migrator.migrate('db/migrate', ENV["VERSION"] ? ENV["VERSION"].to_i : nil )
14
+ end
15
+
16
+ task :environment do
17
+ ActiveRecord::Base.logger = Logger.new(File.open('db/database.log', 'a'))
18
+ ActiveRecord::Base.establish_connection(YAML::load(File.open('db/database.yml')))
19
+ end
20
+
21
+ RSpec::Core::RakeTask.new do |t|
22
+ t.rspec_opts = ["--format Fuubar", "--color"]
23
+ t.pattern = 'spec/**/*_spec.rb'
24
+ end
25
+
26
+ RDoc::Task.new do |rd|
27
+ rd.rdoc_files.include(File.readlines('.document').map(&:strip))
28
+ rd.options + ['-a', '--inline-source', '--charset=UTF-8']
29
+ end
30
+
31
+ task :default=>[:migrate, :spec]
@@ -0,0 +1,4 @@
1
+ == TODO
2
+
3
+ * discover better way to add app/services to load_path
4
+ * discover better way to autoload stuff
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "active_service/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "active_service"
7
+ s.version = ActiveService::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Peter Schröder"]
10
+ s.email = ["ps@nofail.de"]
11
+ s.homepage = "http://nofail.de"
12
+ s.summary = %q{Transactional services for Rails}
13
+ s.description = %q{ActiveService is a Rails extension that aims at creating reusable, transactional services in your application to make your code more DRY.}
14
+
15
+ s.rubyforge_project = "active_service"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency('rake', '~> 0.8.7')
23
+ s.add_dependency('rails', '~> 3.0.0')
24
+
25
+ s.add_development_dependency('sqlite3-ruby', '~> 1.3.3')
26
+ s.add_development_dependency('rspec', '~> 2.6.0')
27
+ s.add_development_dependency('fuubar', '~> 0.0.4')
28
+ s.add_development_dependency('rdoc', '~> 3.6.1')
29
+ end
File without changes
@@ -0,0 +1,2 @@
1
+ adapter: sqlite3
2
+ database: db/test.sqlite3
@@ -0,0 +1,11 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :users do |t|
4
+ t.column :name, :string, :null => false
5
+ end
6
+ end
7
+
8
+ def self.down
9
+ drop_table :users
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ require 'rails'
2
+ require 'active_service/version'
3
+ require 'active_service/configuration'
4
+ require 'active_service/base'
5
+ require 'active_service/action_controller_extension'
6
+ require 'active_service/core_ext/action_controller/base'
7
+ require "yaml"
8
+
9
+ module ActiveService
10
+ module Rails
11
+ class Railtie < ::Rails::Railtie
12
+ config.before_configuration do |app|
13
+ app.config.autoload_paths << ActiveService::Configuration.path
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ module ActionService
2
+ module ActionControllerExtension
3
+
4
+ def service(name)
5
+ self.class.services[name]
6
+ end
7
+
8
+ def __active_services
9
+ self.class.service_names
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ attr_accessor :services, :service_names
15
+
16
+ def service(*names)
17
+ @services ||= {}
18
+ @service_names ||= []
19
+ names.each do |name|
20
+ @services[name] = "#{name.to_s.humanize}Service".constantize.new
21
+ @service_names << name
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ require 'active_record'
2
+
3
+ module ActiveService
4
+ class Base
5
+ def self.method_added(method)
6
+ name = method.to_s
7
+ return if name =~ /__.*/ || name =~ /.*!/
8
+ return if (@__handled_names ||= []).include?(method)
9
+ @__handled_names << method
10
+
11
+ alias_method :"__#{name}", method
12
+ alias_method :"#{name}!", method
13
+
14
+ define_method(method) { |*args, &block| ::ActiveRecord::Base.transaction { self.method(:"__#{name}").call(*args, &block) } }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ module ActiveService
2
+ class Configuration
3
+
4
+ CONFIG_FILE = "config/active_service.yml"
5
+ DEFAULTS = {'path' => 'app/services'}
6
+
7
+ class << self
8
+ def path
9
+ conf['path']
10
+ end
11
+
12
+ private()
13
+
14
+ def conf
15
+ return DEFAULTS unless File.exist?(CONFIG_FILE)
16
+ File.open(CONFIG_FILE) { |file| YAML.load(file) }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,6 @@
1
+ require 'action_controller'
2
+
3
+ class ActionController::Base
4
+ include ActionService::ActionControllerExtension
5
+ extend ActionService::ActionControllerExtension::ClassMethods
6
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveService
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,16 @@
1
+ module ActiveService
2
+ module Generators
3
+ class ConfigGenerator < ::Rails::Generators::Base
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+
6
+ desc "Creates an ActiveService configuration."
7
+ class_option :service
8
+
9
+ def create_config
10
+ create_file "config/active_service.yml", <<-FILE
11
+ path: "lib/services"
12
+ FILE
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ module ActiveService
2
+ module Generators
3
+ class ServiceGenerator < ::Rails::Generators::NamedBase
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+
6
+ desc "Creates an ActiveService class."
7
+ class_option :service
8
+
9
+ def create_service
10
+ create_file "#{ActiveService::Configuration.path}/#{file_name}_service.rb", <<-FILE
11
+ class #{file_name.camelize}Service
12
+ # your service methods
13
+ end
14
+ FILE
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ module ActiveService
4
+ describe ActiveService do
5
+
6
+ context "method extension" do
7
+ it "should have a service method" do
8
+ ActionController::Base.respond_to?(:service).should be_true
9
+ end
10
+ end
11
+
12
+ context "class services" do
13
+ before do
14
+ @controller = TestController.new
15
+ @service = @controller.service(:nups)
16
+ end
17
+
18
+ it "should have services set" do
19
+ @controller.__active_services.should eql([:nups, :dups, :wups])
20
+ end
21
+
22
+ it "should get a service" do
23
+ @service.should be_an_instance_of(NupsService)
24
+ end
25
+
26
+ it "should have a proxy-method" do
27
+ @service.respond_to?(:__some_method).should be_true
28
+ end
29
+
30
+ it "should have a no transactional-method" do
31
+ @service.respond_to?(:some_method!).should be_true
32
+ end
33
+
34
+ it "should call the original method as expected" do
35
+ @service.some_method.should eql('some_value')
36
+ end
37
+
38
+ it "should fail with wrong arity" do
39
+ lambda {@service.testo('bad_param')}.should raise_error
40
+ end
41
+
42
+ it "should handle arity right" do
43
+ @service.with_params('a', 'b').should eql('a b')
44
+ end
45
+
46
+ it "should handle blocks" do
47
+ @service.with_block('a', 'b'){ "nups" }.should eql('["a", "b"]_nups')
48
+ end
49
+
50
+ context "transaction" do
51
+ before do
52
+ User.delete_all
53
+ @user = User.create! :name => 'uschi'
54
+ @user.should eql(User.last)
55
+ end
56
+
57
+ it "should raise an unique error" do
58
+ lambda {User.create! :name => 'uschi'}.should raise_error
59
+ end
60
+
61
+ it "should not commit the transaction on error" do
62
+ expect do
63
+ begin
64
+ @service.transactional_stuff
65
+ rescue
66
+ $!.to_s.should eql('Validation failed: Name has already been taken')
67
+ end
68
+ end.to change(User, :count).by(0)
69
+ end
70
+
71
+ it "should commit the transaction on error" do
72
+ expect do
73
+ begin
74
+ @service.__transactional_stuff
75
+ rescue
76
+ $!.to_s.should eql('Validation failed: Name has already been taken')
77
+ end
78
+ end.to change(User, :count).by(1)
79
+ end
80
+
81
+ end
82
+ end
83
+ end
84
+ end