merimee 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in merimee.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # merimee
2
+
3
+ > Pour parler sans ambiguïté, ce dîner à Sainte-Adresse, près du Havre, malgré les effluves embaumés de la mer, malgré les vins de très bons crus, les cuisseaux de veau et les cuissots de chevreuil prodigués par l’amphitryon, fut un vrai guêpier.
4
+
5
+ _merimee_ adds some `rspec` macros (`Test::Case` to come ... maybe) to add automatic spell checking to your tests.
6
+
7
+ ## Install
8
+
9
+ ```
10
+ gem 'merimee'
11
+ ```
12
+
13
+ ## Configuration
14
+
15
+ The config object has the following methods/arguments :
16
+ ```
17
+ Merime::Checker.new do |config|
18
+ config.dict_add 'OHAI' # Ignore OHAI spelling errors. This is case sensitive for now, feel free to tell me if you feel it shouldn't be the case.
19
+ config.dict_add %w{Trealiu Chtulu} # Ignore other words, method takes any enumerable too !
20
+ config.dict_add_file 'blah.txt' # Ignore all words from blah.txt, one per line
21
+ config.language = 'en' # English by default, but AfterTheDeadline also supports French(fr), Spanish(es), German(de), Portuguese(pt)
22
+ # AtD says that you should provide a key, unique per use. You don't need to register/get it, but
23
+ # you can't have more than one request on their servers at the same time with the same key.
24
+ # Since merimee is intended for test mode, it should be fine.
25
+ config.api_key = 'blah' # See AtD documentation here : it's not needed, the gem is smart enough, especially in test mode
26
+
27
+ config.ignore_types << 'spelling' # You can ignore some types of errors.
28
+ end
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Standalone use
34
+
35
+ Well, you need to initalize a `Merimee::Checker` with a `Merimee::Config`
36
+
37
+ ```ruby
38
+ checker = Merimee::Checker.new do |config|
39
+ # Configure if needed
40
+ end
41
+ # checker = Merimee::Checker.new would be equivalent here
42
+ # checker = Merimee::Checker.new(Merime::Config.new) too
43
+
44
+ checker.check('This text has one BIGE error')
45
+ => [BIGE spelling]
46
+ ```
47
+
48
+ Error objects have some interesting fields (`type`, `suggestions`, `url`, `description`), inspect them to know more.
49
+
50
+ ### Rspec/rails
51
+ Just drop a `require 'merimee'` in your `spec_helper.rb`.
52
+ It gives you the following in your views :
53
+
54
+ ```ruby
55
+ describe 'splash/index' do
56
+ it_should_have_correct_spelling
57
+ end
58
+
59
+ # Which is equivalent to
60
+ describe 'splash/logout' do
61
+ it "should have a correct spelling" do
62
+ render
63
+ rendered.should have_a_correct_spelling
64
+ end
65
+ end
66
+ ```
67
+
68
+ #### Configuration with RSpec
69
+ You can still modify your config within RSpec :
70
+
71
+ ```ruby In your spec_helper.rb
72
+ require 'merimee'
73
+
74
+ RSpec.configure do |config|
75
+ config.merimee_config.dict_add 'ignoreThisWord'
76
+ end
77
+ ```
78
+
79
+ ## Known issues
80
+
81
+ See http://github.com/coutud/merimee/issues
82
+
83
+ ## Thanks etc.
84
+
85
+ This gem was based on some code from https://github.com/msepcot/after_the_deadline
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs.push "lib"
7
+ t.test_files = FileList['test/*_test.rb']
8
+ t.verbose = true
9
+ end
10
+
11
+ desc "Open an irb session preloaded with this library"
12
+ task :console do
13
+ sh "irb -rubygems -I lib -r merimee.rb"
14
+ end
data/lib/merimee.rb ADDED
@@ -0,0 +1,20 @@
1
+ require "merimee/version"
2
+
3
+ require "merimee/checker"
4
+ require "merimee/config"
5
+ require "merimee/after_the_deadline"
6
+
7
+ require 'merimee/railties/merimee_railtie.rb' if defined?(Rails)
8
+
9
+ if defined?(RSpec) && defined?(RSpec::Matchers)
10
+ require 'merimee/rspec/view_checker_helper.rb'
11
+ RSpec.configuration.add_setting :merimee_config
12
+
13
+ RSpec.configure do |config|
14
+ config.merimee_config = Merimee::Config.new
15
+ config.extend Merimee::Rspec::ViewCheckerHelper, :type => :view
16
+ end
17
+ end
18
+
19
+ module Merimee
20
+ end
@@ -0,0 +1,98 @@
1
+ require 'crack'
2
+ require 'net/http'
3
+ require 'uri'
4
+
5
+ class AfterTheDeadline
6
+ BASE_URIS = {
7
+ 'en' => 'http://service.afterthedeadline.com',
8
+ 'fr' => 'http://fr.service.afterthedeadline.com',
9
+ 'de' => 'http://de.service.afterthedeadline.com',
10
+ 'pt' => 'http://pt.service.afterthedeadline.com',
11
+ 'es' => 'http://es.service.afterthedeadline.com'
12
+ }
13
+ BASE_URI = 'http://service.afterthedeadline.com'
14
+
15
+ class << self
16
+ attr_accessor :key, :language
17
+ # Invoke the checkDocument service with provided text.
18
+ #
19
+ # Returns list of AfterTheDeadline::Error objects.
20
+ def check(data)
21
+ results = Crack::XML.parse(perform('/checkDocument', :data => data))['results']
22
+ return [] if results.nil? # we have no errors in our data
23
+
24
+ raise "Server returned an error: #{results['message']}" if results['message']
25
+ errors = if results['error'].kind_of?(Array)
26
+ results['error'].map { |e| AfterTheDeadline::Error.new(e) }
27
+ else
28
+ [AfterTheDeadline::Error.new(results['error'])]
29
+ end
30
+
31
+ return errors
32
+ end
33
+ alias :check_document :check
34
+
35
+ # Invoke the stats service with provided text.
36
+ #
37
+ # Returns AfterTheDeadline::Metrics object.
38
+ def metrics(data)
39
+ results = Crack::XML.parse(perform('/stats', :data => data))['scores']
40
+ return if results.nil? # we have no stats about our data
41
+ AfterTheDeadline::Metrics.new results['metric']
42
+ end
43
+ alias :stats :metrics
44
+
45
+ def perform(action, params)
46
+ uri = URI.parse(BASE_URIS[@language] + action)
47
+ response = Net::HTTP.post_form uri, params.update(:key => @key)
48
+ raise "Unexpected response code from AtD service: #{response.code} #{response.message}" unless response.is_a? Net::HTTPSuccess
49
+ response.body
50
+ end
51
+ end
52
+
53
+ private_class_method :perform
54
+ end
55
+
56
+ class AfterTheDeadline::Error
57
+ attr_reader :string, :description, :precontext, :type, :suggestions, :url
58
+
59
+ def initialize(hash)
60
+ raise "#{self.class} must be initialized with a Hash" unless hash.kind_of?(Hash)
61
+ [:string, :description, :precontext, :type, :url].each do |attribute|
62
+ self.send("#{attribute}=", hash[attribute.to_s])
63
+ end
64
+ self.suggestions = hash['suggestions'].nil? ? [] : [*hash['suggestions']['option']]
65
+ end
66
+
67
+ def info(theme = nil)
68
+ return unless self.url
69
+ uri = URI.parse self.url
70
+ uri.query = (uri.query || '') + "&theme=#{theme}"
71
+ Net::HTTP.get(uri).strip
72
+ end
73
+
74
+ def to_s
75
+ "#{self.string} (#{self.description})"
76
+ end
77
+
78
+ private
79
+ attr_writer :string, :description, :precontext, :type, :suggestions, :url
80
+ end
81
+
82
+ class AfterTheDeadline::Metrics
83
+ attr_reader :spell, :grammer, :stats, :style
84
+
85
+ def initialize(array)
86
+ unless array.kind_of?(Array) && array.all? {|i| i.kind_of?(Hash) }
87
+ raise "#{self.class} must be initialized with an Array of Hashes"
88
+ end
89
+
90
+ self.spell, self.grammer, self.stats, self.style = {}, {}, {}, {}
91
+ array.each do |metric|
92
+ self.send(metric['type'])[metric['key']] = metric['value']
93
+ end
94
+ end
95
+
96
+ private
97
+ attr_writer :spell, :grammer, :stats, :style
98
+ end
@@ -0,0 +1,30 @@
1
+ # Main checker class
2
+ # Heavily inspired by the old AfterTheDeadline github repo
3
+ module Merimee
4
+ class Checker
5
+ attr_reader :config
6
+ def initialize(config = nil)
7
+ @config = config || Config.new
8
+ yield @config if block_given?
9
+ end
10
+
11
+ def check(text, options = {})
12
+ AfterTheDeadline.language = options[:language] || @config.language
13
+ AfterTheDeadline.key = @config.api_key
14
+ errors = AfterTheDeadline.check(text)
15
+
16
+ # Remove any error types we don't care about
17
+ errors.reject! { |e| @config.ignore_types.include?(e.description) }
18
+
19
+ # Remove spelling errors from our custom dictionary
20
+ # Also remove stuff that is obviously not a word (AtD seems to have issues sometime)
21
+ errors.reject! do |e|
22
+ e.type == 'spelling' &&
23
+ @config.dictionary.include?(e.string)
24
+ end
25
+ errors
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,51 @@
1
+ require 'digest/sha1'
2
+
3
+ module Merimee
4
+ DEFAULT_IGNORE_TYPES = ['Bias Language', 'Cliches', 'Complex Expression', 'Diacritical Marks', 'Double Negatives', 'Hidden Verbs', 'Jargon Language', 'Passive voice', 'Phrases to Avoid', 'Redundant Expression']
5
+ DEFAULT_LANGUAGE = 'en'
6
+
7
+ class Config
8
+ attr_reader :dictionary
9
+ attr_accessor :ignore_types
10
+
11
+ # This is required by the service for caching purposes,
12
+ # but no registration is needed (api_key should just be unique for
13
+ # your app, only one request can happen per key at any moment).
14
+ # There shouldn't be any need for this, but still
15
+ attr_accessor :api_key
16
+ # The language to use for this check
17
+ # This is used to select the proper AtD server, unless overriden with the
18
+ # :language option
19
+ attr_accessor :language
20
+
21
+ def initialize
22
+ @dictionary = []
23
+ @ignore_types = Merimee::DEFAULT_IGNORE_TYPES
24
+ @api_key = Config.generate_api_key
25
+ @language = Merimee::DEFAULT_LANGUAGE
26
+ end
27
+
28
+ def dict_add(word)
29
+ if word.kind_of?(String)
30
+ @dictionary << word
31
+ elsif word.kind_of?(Enumerable)
32
+ @dictionary.concat word
33
+ end
34
+ end
35
+
36
+ def dict_add_file(file)
37
+ File.open(file) {|f| dict_add(f.readlines.map(&:strip)) }
38
+ end
39
+
40
+ private
41
+ # We just use the path to this file, and the current hostname
42
+ # If you
43
+ # This is probably not totally unique
44
+ # We use the mac address and this file path, should be unique enough
45
+ def self.generate_api_key
46
+ data = [File.expand_path(__FILE__)]
47
+ data << Mac.addr if defined?(Mac)
48
+ Digest::SHA1.hexdigest(data.join)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,7 @@
1
+ module Merimee
2
+ class Railtie < ::Rails::Railtie
3
+ rake_tasks do
4
+ require 'path/to/railtie.tasks'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,36 @@
1
+ module Merimee
2
+ module Rspec
3
+ module ViewCheckerHelper
4
+ RSpec::Matchers.define :have_a_correct_spelling do
5
+ match do |rendered|
6
+ checker = Merimee::Checker.new(::RSpec.configuration.merimee_config)
7
+
8
+ if rendered
9
+ @errors = checker.check(rendered)
10
+ @errors.empty?
11
+ else
12
+ true
13
+ end
14
+ end
15
+ failure_message_for_should do |view|
16
+ errs = []
17
+ @errors.each do |err|
18
+ message = "[#{err.type}] #{err.string}"
19
+ if err.suggestions
20
+ message << " (suggested: #{err.suggestions.join(', ')})"
21
+ end
22
+ errs << message
23
+ end
24
+ errs.join("\n")
25
+ end
26
+ end
27
+
28
+ def it_should_have_a_correct_spelling
29
+ it "should have a correct spelling" do
30
+ render
31
+ rendered.should have_a_correct_spelling
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module Merimee
2
+ VERSION = "0.0.1"
3
+ end
data/merimee.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "merimee/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "merimee"
7
+ s.version = Merimee::VERSION
8
+ s.authors = ["coutud"]
9
+ s.email = ["wam@atwam.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Add spell checking to your views tests/specs}
12
+ s.description = %q{Automatically submit your rendered views to AfterTheDeadline free spell check service, and make sure you don't have any errors in your views !}
13
+
14
+ s.rubyforge_project = "merimee"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_runtime_dependency 'crack'
23
+
24
+ s.add_development_dependency 'minitest'
25
+ # s.add_development_dependency "rspec"
26
+ # s.add_runtime_dependency "rest-client"
27
+ end
@@ -0,0 +1,26 @@
1
+ require 'minitest/autorun'
2
+ require 'merimee'
3
+
4
+ require 'digest/sha1'
5
+
6
+ class CheckerTest < MiniTest::Unit::TestCase
7
+ ATD_SLEEP = 0.7
8
+ def setup
9
+ @checker = Merimee::Checker.new
10
+ end
11
+
12
+ def test_check_perfect_text
13
+ sleep ATD_SLEEP # Make sure we sleep a bit, AtD returns 503 if requests are too close
14
+ errors = @checker.check("Perfect text")
15
+ assert_empty errors
16
+ end
17
+
18
+ def test_text_with_errors
19
+ erroneous_text = "Wrong text garanteed with errors"
20
+ sleep ATD_SLEEP # Make sure we sleep a bit, AtD returns 503 if requests are too close
21
+ errors = @checker.check(erroneous_text)
22
+
23
+ refute_empty errors
24
+ assert_equal "garanteed", errors.first.string
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ require 'minitest/autorun'
2
+ require 'merimee'
3
+
4
+ class ConfigTest < MiniTest::Unit::TestCase
5
+ def setup
6
+ @config = Merimee::Config.new
7
+ end
8
+
9
+ def test_ignore_type_not_empty_by_default
10
+ refute_empty @config.ignore_types
11
+ end
12
+
13
+ def test_add_dict_with_word
14
+ @config.dict_add "OHAI"
15
+ assert_includes @config.dictionary, "OHAI"
16
+ end
17
+
18
+ def test_add_dict_with_array
19
+ @config.dict_add "OHAI"
20
+ @config.dict_add ["Foo", "Bar"]
21
+ assert_equal ["OHAI", "Foo", "Bar"], @config.dictionary
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: merimee
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - coutud
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: crack
16
+ requirement: &70150877255760 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70150877255760
25
+ - !ruby/object:Gem::Dependency
26
+ name: minitest
27
+ requirement: &70150877255200 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70150877255200
36
+ description: Automatically submit your rendered views to AfterTheDeadline free spell
37
+ check service, and make sure you don't have any errors in your views !
38
+ email:
39
+ - wam@atwam.com
40
+ executables: []
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - README.md
47
+ - Rakefile
48
+ - lib/merimee.rb
49
+ - lib/merimee/after_the_deadline.rb
50
+ - lib/merimee/checker.rb
51
+ - lib/merimee/config.rb
52
+ - lib/merimee/railties/merimee_railtie.rb
53
+ - lib/merimee/rspec/view_checker_helper.rb
54
+ - lib/merimee/version.rb
55
+ - merimee.gemspec
56
+ - test/checker_test.rb
57
+ - test/config_test.rb
58
+ homepage: ''
59
+ licenses: []
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project: merimee
78
+ rubygems_version: 1.8.10
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Add spell checking to your views tests/specs
82
+ test_files:
83
+ - test/checker_test.rb
84
+ - test/config_test.rb