clearest 0.0.1a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in clearest.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ clearest Copyright (c) 2012-2013 Ultragreen Software, Romain GEORGES
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions
6
+ are met:
7
+ 1. Redistributions of source code must retain the above copyright
8
+ notice, this list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright
10
+ notice, this list of conditions and the following disclaimer in the
11
+ documentation and/or other materials provided with the distribution.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23
+ SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Clearest
2
+
3
+ Clearest : Ruby Rack RESTfull services generator
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'clearest'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install clearest
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( http://github.com/<my-github-username>/clearest/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,62 @@
1
+ #require 'bundler_geminabox/gem_tasks'
2
+ require "bundler/gem_tasks"
3
+ require 'rubygems'
4
+ require 'rspec'
5
+ require 'rake'
6
+ require "rake/clean"
7
+ require "rubygems/package_task"
8
+ require "rdoc/task"
9
+ require 'code_statistics'
10
+ require 'rspec/core/rake_task'
11
+ require 'yard'
12
+ require 'yard/rake/yardoc_task.rb'
13
+ require "rake/tasklib"
14
+ require "roodi"
15
+ require "roodi_task"
16
+ require 'rake/version_task'
17
+
18
+ RoodiTask.new() do | t |
19
+ t.patterns = %w(lib/**/*.rb)
20
+ t.config = "ultragreen_roodi_coding_convention.yml"
21
+ end
22
+
23
+
24
+ CLEAN.include('*.tmp','*.old')
25
+ CLOBBER.include('*.tmp', 'build/*','#*#')
26
+
27
+
28
+ content = File::readlines(File.join(File.dirname(__FILE__), 'clearest.gemspec')).join
29
+ spec = eval(content)
30
+
31
+ RSpec::Core::RakeTask.new('spec')
32
+
33
+ YARD::Rake::YardocTask.new do |t|
34
+ t.files = [ 'lib/**/*.rb', '-', 'doc/**/*','spec/**/*_spec.rb']
35
+ t.options += ['--title', "Gem Documentation"]
36
+ t.options += ['-o', "yardoc"]
37
+ end
38
+ YARD::Config.load_plugin('yard-rspec')
39
+
40
+ namespace :yardoc do
41
+ task :clobber do
42
+ rm_r "yardoc" rescue nil
43
+ rm_r ".yardoc" rescue nil
44
+ end
45
+ end
46
+ task :clobber => "yardoc:clobber"
47
+
48
+
49
+ Gem::PackageTask.new(spec) do |pkg|
50
+ pkg.need_tar = true
51
+ pkg.need_zip = true
52
+ end
53
+
54
+ Rake::RDocTask.new('rdoc') do |d|
55
+ d.rdoc_files.include('doc/**/*','bin/*')
56
+ d.title = 'Dorsal : Yard'
57
+ d.options << '--line-numbers' << '--diagram' << '-SHN'
58
+ end
59
+
60
+ task :default => [:gem]
61
+
62
+ Rake::VersionTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.01a
data/bin/clearest ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+ require 'clearest'
3
+ require 'thor'
4
+
5
+ module CleaRESTCLI
6
+ class Application < Thor
7
+ desc "new <name> <dsn>", "Adds a new CleaREST application"
8
+ def new(name,dsn)
9
+ Clearest::ApplicationBuilder::new :project_name => name, :target_path => '.', :dsn => dsn
10
+ end
11
+
12
+ desc "show", "Display information on the current application"
13
+ def show
14
+ end
15
+ end
16
+
17
+
18
+ class Services < Thor
19
+ desc "add <table>", "Adds the new service named <table> to the current application"
20
+ def add(table)
21
+ app = Clearest::Validators::Applications::check :path => '.'
22
+ Clearest::ServiceBuilder::new :table_name => table, :dsn => app[:dsn], :project_name => app[:project_name], :target_path => '.'
23
+ end
24
+ desc "remove <name>", "remove service <name> of the current application"
25
+ def remove(name)
26
+
27
+ end
28
+
29
+ end
30
+
31
+ class CleaREST < Thor
32
+ desc "application SUBCOMMAND ...ARGS", "manage CleaREST application"
33
+ subcommand "application", Application
34
+ desc "services SUBCOMMAND ...ARGS", "manage services in application"
35
+ subcommand "services", Services
36
+ end
37
+ end
38
+
39
+ CleaRESTCLI::CleaREST.start(ARGV)
40
+
41
+
data/clearest.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'clearest/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "clearest"
8
+ spec.version = Clearest::VERSION
9
+ spec.authors = ["Romain GEORGES"]
10
+ spec.email = ["romain@ultragreen.net"]
11
+ spec.summary = %q{Clearest: REST Api/crud generator }
12
+ spec.description = %q{Clearest: command line tools for REST (Rack/Sinatra) API generation}
13
+ spec.homepage = "http://github.com/Ultragreen/clearest"
14
+ spec.license = "BSD-2"
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.5"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency 'rspec', '~> 2.14', '>= 2.14.1'
23
+ spec.add_development_dependency 'yard', '~> 0.8', '>= 0.8.7.2'
24
+ spec.add_development_dependency 'rdoc', '~> 4.0', '>= 4.0.1'
25
+ spec.add_development_dependency 'roodi', '~> 3.1', '>= 3.1.1'
26
+ spec.add_development_dependency 'code_statistics', '~> 0.2', '>= 0.2.13'
27
+ spec.add_development_dependency 'yard-rspec', '~> 0.1'
28
+
29
+
30
+ spec.add_dependency 'dm-is-reflective', '= 1.2.0'
31
+ spec.add_dependency 'colorize', '~> 0.7', '>= 0.7.7'
32
+ spec.add_dependency 'dm-sqlite-adapter', '~> 1.2', '>= 1.2.0'
33
+ # spec.add_development_dependency "bundler_geminabox", "~> 0.2",'>= 0.2.0'
34
+ spec.add_development_dependency "version", "~> 1.0",'>= 1.0.0'
35
+
36
+ end
data/lib/clearest.rb ADDED
@@ -0,0 +1,20 @@
1
+ require "clearest/version"
2
+ require 'template'
3
+ require 'pp'
4
+ require 'date'
5
+ require 'data_mapper'
6
+ require 'dm-is-reflective'
7
+ require 'dm-core'
8
+ require 'colorize'
9
+ require 'dm-sqlite-adapter'
10
+
11
+
12
+
13
+
14
+ Dir[File.dirname(__FILE__) + '/clearest/patches/*_patch.rb'].sort.each {|file| require file }
15
+ Dir[File.dirname(__FILE__) + '/clearest/generators/*.rb'].sort.each {|file| require file }
16
+ Dir[File.dirname(__FILE__) + '/clearest/builders/*.rb'].sort.each {|file| require file }
17
+ Dir[File.dirname(__FILE__) + '/clearest/validators/*.rb'].sort.each {|file| require file }
18
+
19
+
20
+
@@ -0,0 +1,13 @@
1
+ module Clearest
2
+ class ApplicationBuilder
3
+ def initialize(_options = {})
4
+ _options[:template_map] = {
5
+ 'main.rb' => [:dsn]
6
+ }
7
+ Clearest::FoldersGenerator::new _options
8
+ Clearest::FilesCopyGenerator::new _options
9
+ Clearest::TemplatesGenerator::new _options
10
+ File.open("#{_options[:target_path]}/#{_options[:project_name]}/application.yml", 'w') {|f| f.write _options.to_yaml }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module Clearest
2
+ class ServiceBuilder
3
+ def initialize(_options = {})
4
+ _options[:template_map] = {
5
+ 'models/TABLE_NAME.rb' => [:model_definition],
6
+ 'routes/TABLE_NAME.rb' => [:prefix,:table_name,:model_name],
7
+ 'spec/TABLE_NAME_spec.rb' => [:prefix,:table_name,:model_name,:test_db_filename,:first_obj,:second_obj,:updated_field,:new_value]
8
+ }
9
+ Clearest::TemplatesGenerator::new _options
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Clearest
2
+ class AbstractGenerator
3
+ def initialize(_options = {})
4
+ @options = _options
5
+ @template_root = File.expand_path("../../../template", __FILE__)
6
+ @@dm ||= DataMapper.setup(:default, _options[:dsn])
7
+ @options[:list_folder] = Dir.glob("#{@template_root}/*").reject {|folder| (File::file? folder) }.map {|folder| folder.split('/').last }
8
+ @options[:list_folder] << '.'
9
+ @options[:prefix] ||= '/api'
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ module Clearest
2
+ class FilesCopyGenerator < AbstractGenerator
3
+ def initialize(_options = {})
4
+ super _options
5
+ @options[:list_folder].each do |folder|
6
+ files = Dir.glob("#{@template_root}/#{folder}/*").reject {|file| (file =~ /.tpl$/ or File::directory? file) }
7
+ files.each do |file|
8
+ fullpath = "#{@options[:target_path]}/#{@options[:project_name]}/#{folder}"
9
+ puts " #{'copying'.colorize(:light_green)} (file) #{fullpath}/#{File.basename(file)}"
10
+ FileUtils::copy file, fullpath
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,92 @@
1
+ module Clearest
2
+ class AutoFixtures < AbstractGenerator
3
+
4
+ FLOAT_MAX = 256
5
+ INT_MAX = 256
6
+ STRING_MAX_LENGTH = 100
7
+
8
+ attr_reader :fixtures
9
+
10
+ def initialize(_options = {})
11
+ super(_options)
12
+ generate
13
+ end
14
+
15
+
16
+ private
17
+ def random_string(length=10)
18
+ chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ0123456789'
19
+ password = ''
20
+ length.times { password << chars[rand(chars.size)] }
21
+ password
22
+ end
23
+
24
+
25
+ def fp_rand(limit)
26
+ fl = limit.floor
27
+ rm = limit.remainder(fl)
28
+ rand(fl) + rand(rm)
29
+ end
30
+
31
+ def ignore
32
+ end
33
+
34
+ def mapping(_options = { :field => 'default',:type => 'String', :length => STRING_MAX_LENGTH})
35
+ type_map = { 'DateTime' => DateTime.now,
36
+ 'String' => "#{random_string(_options[:length]-(_options[:field].size + 1))}_#{_options[:field]}",
37
+ 'Text' => "#{random_string(STRING_MAX_LENGTH- (_options[:field].size + 1))}_#{_options[:field]}",
38
+ 'Serial' => ignore,
39
+ 'Boolean' => true,
40
+ 'Float' => fp_rand(FLOAT_MAX),
41
+ 'Decimal' => fp_rand(FLOAT_MAX),
42
+ 'Integer' => rand(INT_MAX)
43
+ }
44
+ return type_map[_options[:type]]
45
+ end
46
+
47
+ def generate
48
+ @fixtures = Hash::new
49
+ @@dm.fields(@options[:table_name]).each do |content|
50
+ field,type,spec = content
51
+ type = type.to_s.split('::').last
52
+ length = (spec[:length])? spec[:length] : STRING_MAX_LENGTH
53
+ fixture = mapping :type => type, :length => length, :field => field
54
+ @fixtures[field] = {:type => type, :length => length, :value => fixture } unless fixture.nil?
55
+ end
56
+ @fixtures
57
+ end
58
+
59
+ public
60
+
61
+
62
+ def get_value_for(_options = {})
63
+ field = _options[:field]
64
+ field_spec = @fixtures[field]
65
+ return mapping :type => field_spec[:type], :length => field_spec[:length], :field => field
66
+ end
67
+
68
+ def get_field(_options = { :type => ['String','Integer','Text','Float'] })
69
+ @test_map = Hash::new
70
+ @fixtures.each {|item,content| @test_map[item] = content[:type] }
71
+ _options[:type].each do |type|
72
+ res = @test_map.rassoc type
73
+ return res unless res.nil?
74
+ end
75
+ return false
76
+ end
77
+
78
+ def to_s
79
+ res = "{\n"
80
+ records = Array::new
81
+ @fixtures.each do |field,content|
82
+ if ['Float','Decimal','Integer'].include? content[:type] then
83
+ records << " :#{field} => #{content[:value]}"
84
+ else
85
+ records << " :#{field} => \"#{content[:value]}\""
86
+ end
87
+ end
88
+ res << records.join(",\n")
89
+ res << "\n }\n"
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,13 @@
1
+ module Clearest
2
+ class FoldersGenerator < AbstractGenerator
3
+ def initialize(_options = {})
4
+ super _options
5
+ @options[:list_folder].each do |folder|
6
+ next if folder == '.'
7
+ fullpath = "#{@options[:target_path]}/#{@options[:project_name]}/#{folder}"
8
+ FileUtils.mkdir_p fullpath
9
+ puts " #{'creating'.colorize(:light_green)} (folder) #{fullpath}"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ module Clearest
2
+ class TemplatesGenerator < AbstractGenerator
3
+ def initialize(_options = {})
4
+ super _options
5
+ fullpath = "#{@options[:target_path]}"
6
+ @options[:test_db_filename] ||= '/tmp/clearest.db'
7
+ @options[:template_map].each do |template,list|
8
+ file_target = (@options[:table_name])? template.sub('TABLE_NAME', @options[:table_name]) : template
9
+ generator = Template::new :list_token => list, :template_file => "#{@template_root}/#{template}.tpl"
10
+ @options[:model_name] = DataMapper::Inflector.singularize _options[:table_name].capitalize if _options[:table_name]
11
+ _data = @options.dup
12
+ _data.each {|item,value| _data.delete item unless list.include? item.to_sym}
13
+ if template == 'models/TABLE_NAME.rb' then
14
+ models = @@dm.auto_genclass! :storages => @options[:table_name], :scope => Object
15
+ _data[:model_definition] = models.first.to_source
16
+ end
17
+ fixtures = AutoFixtures::new(_options) if _options[:table_name]
18
+ if template == 'spec/TABLE_NAME_spec.rb' then
19
+ [:first_obj,:second_obj].each do |obj|
20
+ _data[obj] = fixtures.to_s
21
+ end
22
+ field,type = fixtures.get_field
23
+ new_fixture = fixtures.get_value_for :field => field
24
+ _data[:updated_field] = ":#{field.to_s}"
25
+ if ['Float','Decimal','Integer'].include? type then
26
+ _data[:new_value] = "#{new_fixture}"
27
+ else
28
+ _data[:new_value] = "'#{new_fixture}'"
29
+ end
30
+ end
31
+ generator.map _data
32
+ puts " #{'staging'.colorize(:light_green)} (template) #{fullpath}/#{file_target}"
33
+ File.open("#{fullpath}/#{file_target}", 'w') { |file| file.write(generator.output) }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,36 @@
1
+ module DmIsReflective::SqliteAdapterPatch
2
+ def reflective_lookup_primitive primitive
3
+ case primitive.upcase
4
+ when 'INTEGER' ; Integer
5
+ when 'REAL', 'NUMERIC', 'FLOAT'; Float
6
+ when 'VARCHAR' ; String
7
+ when 'TIMESTAMP' ; DateTime
8
+ when 'BOOLEAN' ; Property::Boolean
9
+ when 'TEXT' ; Property::Text
10
+ else Property::Text
11
+ end || super(primitive)
12
+ end
13
+ end
14
+
15
+
16
+ module DmIsReflective::ClassMethod
17
+ def to_source scope=nil
18
+
19
+ <<-RUBY
20
+ class #{scope}::#{name} < #{superclass}
21
+ include DataMapper::Resource
22
+ #{
23
+ properties.map do |prop|
24
+ hash = prop.options.dup
25
+ hash.delete :scale if hash.include? :scale and hash[:scale].nil?
26
+ "property :#{prop.name}, #{prop.class.name}, #{hash}"
27
+ end.join("\n")
28
+ }
29
+ end
30
+ RUBY
31
+ end
32
+ end
33
+
34
+ DataMapper::Adapters.const_get("SqliteAdapter").__send__(:include,
35
+ DmIsReflective::SqliteAdapterPatch)
36
+ DataMapper::Model.append_extensions(DmIsReflective)
@@ -0,0 +1,10 @@
1
+ require 'yaml'
2
+ module Clearest
3
+ module Validators
4
+ module Applications
5
+ def Applications::check(_options = {})
6
+ conf = YAML.load_file('application.yml')
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ require 'version'
2
+ module Clearest
3
+ is_versioned
4
+ end
data/lib/template.rb ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require 'rubygems'
5
+ require 'methodic'
6
+
7
+
8
+ class Template
9
+
10
+ attr_reader :list_token
11
+ attr_reader :template_file
12
+ attr_reader :content
13
+
14
+ def initialize(_options)
15
+ options = Methodic.get_options(_options)
16
+ options.specify_classes_of({:list_token => Array, :template_file => String})
17
+ options.specify_presences_of([:list_token,:template_file])
18
+ options.validate!
19
+
20
+
21
+ @template_file = _options[:template_file]
22
+ raise NoTemplateFile::new('No template file found') unless File::exist?(@template_file)
23
+ begin
24
+ @content = IO::readlines(@template_file).join.chomp
25
+ rescue
26
+ raise NoTemplateFile::new('No template file found')
27
+ end
28
+ token_from_template = @content.scan(/%%(\w+)%%/).flatten.uniq.map{ |item| item.downcase.to_sym}
29
+ begin
30
+ @list_token = _options[:list_token].map!{|_token| _token.downcase.to_sym }
31
+ @hash_token = Hash::new; @list_token.each{|_item| @hash_token[_item.to_s] = String::new('')}
32
+ rescue
33
+ raise InvalidTokenList::new("Token list malformation")
34
+ end
35
+ raise InvalidTokenList::new("Token list doesn't match the template") unless token_from_template.sort == @list_token.sort
36
+ @list_token.each{|_token| eval("def #{_token}=(_value); raise ArgumentError::new('Not a String') unless _value.class == String; @hash_token['#{_token}'] = _value ;end")}
37
+ @list_token.each{|_token| eval("def #{_token}; @hash_token['#{_token}'] ;end")}
38
+ end
39
+
40
+ def token(_token,_value)
41
+ raise ArgumentError::new('Not a String') unless _value.class == String
42
+ @hash_token[_token.to_s] = _value
43
+ end
44
+
45
+
46
+ def map(_hash)
47
+ _data = {}
48
+ _hash.each { |item,val|
49
+ raise ArgumentError::new("#{item} : Not a String") unless val.class == String
50
+ _data[item.to_s.downcase] = val
51
+ }
52
+ raise InvalidTokenList::new("Token list malformation") unless _data.keys.sort == @list_token.map{|_token| _token.to_s }.sort
53
+ @hash_token = _data
54
+ end
55
+
56
+ def method_missing(_name,*_args)
57
+ raise NotAToken
58
+ end
59
+
60
+
61
+ def output
62
+ _my_res = String::new('')
63
+ _my_res = @content
64
+ @list_token.each{|_token|
65
+ _my_res.gsub!(/%%#{_token.to_s.upcase}%%/,@hash_token[_token.to_s])
66
+ }
67
+ return _my_res
68
+ end
69
+
70
+ end
71
+
72
+ class InvalidTokenList < Exception; end
73
+ class NotAToken < Exception; end
74
+ class NoTemplateFile < Exception; end
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'sinatra', '1.4.4'
4
+ gem 'json', '1.7.4'
5
+ gem 'data_mapper', '1.2.0'
6
+ gem 'dm-sqlite-adapter','1.2.0'
7
+ gem 'dm-postgres-adapter', '1.2.0'
8
+ gem "rack-rest-rspec"
9
+ gem 'rake'
10
+ gem 'rspec'
11
+ gem 'rack-test'
12
+ gem 'roodi'
13
+ gem 'code_statistics'
@@ -0,0 +1,2 @@
1
+ # Rest service with Sinatra + Datamapper
2
+ # Generated by CleaREST (c) romain GEORGES
@@ -0,0 +1,37 @@
1
+ require 'dm-migrations'
2
+ require 'rspec'
3
+ require 'rspec/core/rake_task'
4
+ require "roodi"
5
+ require "roodi_task"
6
+ require 'rake'
7
+ require "rake/clean"
8
+
9
+ ENV['DIRECTORIES_TO_CALCULATE'] = 'routes/,helpers/,models/'
10
+ require 'code_statistics'
11
+
12
+ RoodiTask.new() do | t |
13
+ t.patterns = %w(**/*.rb)
14
+ t.config = "roodi.yml"
15
+ end
16
+
17
+ CLEAN.include('*.tmp','*.old')
18
+ CLOBBER.include('*.tmp', 'build/*','#*#')
19
+
20
+ RSpec::Core::RakeTask.new(:spec)
21
+
22
+ desc "List all routes for this application"
23
+ task :routes do
24
+ puts `grep '^[get|post|put|delete].*do$' routes/*.rb | sed 's/ do$//'`
25
+ end
26
+
27
+ desc "auto migrates the database"
28
+ task :migrate do
29
+ require './main'
30
+ DataMapper.auto_migrate!
31
+ end
32
+
33
+ desc "auto upgrades the database"
34
+ task :upgrade do
35
+ require './main'
36
+ DataMapper.auto_upgrade!
37
+ end
@@ -0,0 +1,2 @@
1
+ require './main'
2
+ run Sinatra::Application
@@ -0,0 +1,9 @@
1
+ class Hash
2
+ def symbolize!
3
+ self.keys.each do |key|
4
+ self[key.to_sym] = self.delete(key)
5
+ end
6
+ return ahash
7
+ end
8
+
9
+ end
@@ -0,0 +1,3 @@
1
+ # encoding: UTF-8
2
+ Dir[File.dirname(__FILE__) + '/*.rb'].each {|file| require file unless File.basename(file) == 'init.rb'}
3
+
@@ -0,0 +1,27 @@
1
+ # encoding: UTF-8
2
+ require 'sinatra/base'
3
+
4
+ module Sinatra
5
+ module ResponseFormat
6
+ def format_response(data, format)
7
+ response = case format
8
+ when 'text/xml' then data.to_xml
9
+ when 'application/json' then data.to_json
10
+ when 'text/x-yaml' then data.to_yaml
11
+ when 'text/csv' then data.to_csv
12
+ else data.to_json
13
+ end
14
+ return response
15
+ end
16
+
17
+ def format_by_extensions(extension)
18
+ result = {
19
+ 'xml' => 'text/xml',
20
+ 'json' => 'application/json',
21
+ 'yaml' => 'text/x-yaml',
22
+ 'csv' => 'text/csv'}
23
+ return result[extension]
24
+ end
25
+ end
26
+ helpers ResponseFormat
27
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: UTF-8
2
+ require 'json'
3
+ require 'sinatra'
4
+ require 'data_mapper'
5
+ require 'dm-migrations'
6
+
7
+
8
+ config = {
9
+ :development => {
10
+ :debug => true,
11
+ :dsn => '%%DSN%%'
12
+ },
13
+ :test => {
14
+ :debug => false,
15
+ :dsn => '%%DSN%%'
16
+ },
17
+ :production => {
18
+ :debug => false,
19
+ :dsn => '%%DSN%%'
20
+ }
21
+ }
22
+
23
+ config.each do |env,params|
24
+
25
+ configure env do
26
+ DataMapper::Logger.new($stdout, :debug) if params[:debug]
27
+ DataMapper.setup( :default, params[:dsn])
28
+ end
29
+ end
30
+
31
+ require './models/init'
32
+ require './helpers/init'
33
+ require './routes/init'
34
+
35
+ DataMapper.finalize
@@ -0,0 +1,2 @@
1
+ # encoding: UTF-8
2
+ %%MODEL_DEFINITION%%
@@ -0,0 +1,2 @@
1
+ # encoding: UTF-8
2
+ Dir[File.dirname(__FILE__) + '/*.rb'].each {|file| require file unless File.basename(file) == 'init.rb'}
@@ -0,0 +1,25 @@
1
+ AssignmentInConditionalCheck:
2
+ CaseMissingElseCheck:
3
+ ClassLineCountCheck:
4
+ line_count: 300
5
+ ClassNameCheck:
6
+ pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
7
+ #ClassVariableCheck:
8
+ CyclomaticComplexityBlockCheck:
9
+ complexity: 5
10
+ CyclomaticComplexityMethodCheck:
11
+ complexity: 10
12
+ EmptyRescueBodyCheck:
13
+ ForLoopCheck:
14
+ MethodLineCountCheck:
15
+ line_count: 30
16
+ MethodNameCheck:
17
+ pattern: !ruby/regexp /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
18
+ # MissingForeignKeyIndexCheck:
19
+ ModuleLineCountCheck:
20
+ line_count: 500
21
+ ModuleNameCheck:
22
+ pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
23
+ ParameterNumberCheck:
24
+ parameter_count: 5
25
+
@@ -0,0 +1,33 @@
1
+ # encoding: UTF-8
2
+
3
+ get '%%PREFIX%%/%%TABLE_NAME%%.?:format?' do
4
+ format_response(%%MODEL_NAME%%.all, (params[:format])? format_by_extensions(params[:format]): request.accept.first)
5
+ end
6
+
7
+ get '%%PREFIX%%/%%TABLE_NAME%%/:id.?:format?' do
8
+ obj ||= %%MODEL_NAME%%.get(params[:id]) || halt(404)
9
+ format_response(obj, (params[:format])? format_by_extensions(params[:format]): request.accept.first)
10
+ end
11
+
12
+ post '%%PREFIX%%/%%TABLE_NAME%%.?:format?' do
13
+ is_raw = request.content_type.to_s.downcase.eql?('application/x-www-form-urlencoded')
14
+ body = (is_raw)? request.POST() : JSON.parse(request.body.read)
15
+ obj = %%MODEL_NAME%%.create(body)
16
+ status 201
17
+ format_response(obj, (params[:format])? format_by_extensions(params[:format]): request.accept.first)
18
+ end
19
+
20
+ put '%%PREFIX%%/%%TABLE_NAME%%/:id.?:format?' do
21
+ is_raw = request.content_type.to_s.downcase.eql?('application/x-www-form-urlencoded')
22
+ body = (is_raw)? request.POST() : JSON.parse(request.body.read)
23
+ obj ||= %%MODEL_NAME%%.get(params[:id]) || halt(404)
24
+ body.symbolize!
25
+ obj.attributes = obj.attributes.merge(body)
26
+ halt 500 unless obj.save!
27
+ format_response(obj, (params[:format])? format_by_extensions(params[:format]): request.accept.first)
28
+ end
29
+
30
+ delete '%%PREFIX%%/%%TABLE_NAME%%/:id' do
31
+ obj ||= %%MODEL_NAME%%.get(params[:id]) || halt(404)
32
+ halt 500 unless obj.destroy
33
+ end
@@ -0,0 +1,2 @@
1
+ # encoding: UTF-8
2
+ Dir[File.dirname(__FILE__) + '/*.rb'].each {|file| require file unless File.basename(file) == 'init.rb'}
@@ -0,0 +1,82 @@
1
+ ENV['RACK_ENV'] = 'test'
2
+
3
+ require './main'
4
+ require './spec/spec_helper.rb'
5
+
6
+
7
+ describe '%%MODEL_NAME%% REST CRUD API' do
8
+ before :all do
9
+ base_file = '%%TEST_DB_FILENAME%%'
10
+ File::unlink(base_file) if File::exist?(base_file)
11
+ DataMapper.auto_migrate!
12
+ $service = RestCRUDService::new :object => '%%TABLE_NAME%%'
13
+ $first_obj = %%FIRST_OBJ%%
14
+ $second_obj = %%SECOND_OBJ%%
15
+ end
16
+
17
+ subject { $service }
18
+ context "POST %%PREFIX%%/%%TABLE_NAME%% : create a new record" do
19
+ it { expect(subject.create_record($first_obj)).to be_correctly_sent }
20
+ it { expect(subject).to respond_with_status 201 }
21
+ end
22
+
23
+ context "POST %%PREFIX%%/%%TABLE_NAME%% : create an other record" do
24
+ it { expect(subject.create_record($second_obj)).to be_correctly_sent }
25
+ it { expect(subject).to respond_with_status 201 }
26
+ end
27
+
28
+ context "GET %%PREFIX%%/%%TABLE_NAME%% : retrieve all records" do
29
+ before do
30
+ $first_obj[:id] = 1
31
+ $second_obj[:id] = 2
32
+ end
33
+ it { expect(subject.retrieve_all_records).to be_correctly_sent }
34
+ it { expect(subject).to respond_with_status 200 }
35
+ it { expect(subject).to respond_a_collection_of_record }
36
+ it { expect(subject).to respond_with_data [$first_obj,$second_obj] }
37
+ it { expect(subject).to respond_with_collection_size 2 }
38
+ end
39
+
40
+ context 'GET %%PREFIX%%/%%TABLE_NAME%%/2 : retrieve the second record' do
41
+ it { expect(subject.retrieve_record(2)).to be_correctly_sent }
42
+ it { expect(subject).to respond_with_status 200 }
43
+ it { expect(subject).to respond_a_record }
44
+ it { expect(subject).to respond_with_data $second_obj }
45
+ end
46
+
47
+ context 'PUT %%PREFIX%%/%%TABLE_NAME%%/2 : update the second record ' do
48
+ it { expect(subject.update_record(2,{ %%UPDATED_FIELD%% => %%NEW_VALUE%%})).to be_correctly_sent }
49
+ it { expect(subject).to respond_with_status 200 }
50
+ end
51
+
52
+ context 'DELETE %%PREFIX%%/%%TABLE_NAME%%/1 : delete the first record ' do
53
+ it { expect(subject.destroy_record(1)).to be_correctly_sent }
54
+ it { expect(subject).to respond_with_status 200 }
55
+ end
56
+
57
+ context 'GET %%PREFIX%%/%%TABLE_NAME%% : retrieve all records ' do
58
+ before do
59
+ # todo mod record for first not default string field
60
+ $second_obj[%%UPDATED_FIELD%%] = %%NEW_VALUE%%
61
+ end
62
+ it { expect(subject.retrieve_all_records).to be_correctly_sent }
63
+ it { expect(subject).to respond_with_status 200 }
64
+ it { expect(subject).to respond_a_collection_of_record }
65
+ it { expect(subject).to respond_with_collection_size 1 }
66
+ it { expect(subject).to respond_with_data $second_obj }
67
+ end
68
+
69
+ context 'GET %%PREFIX%%/%%TABLE_NAME%%/1 : failed to retrieve the first record' do
70
+ it { expect(subject.retrieve_record(1)).to be_correctly_sent }
71
+ it { expect(subject).to respond_with_status 404 }
72
+ it { expect(subject).to_not respond_with_data $first_obj }
73
+ end
74
+
75
+ context 'GET %%PREFIX%%/%%TABLE_NAME%%/2 : retrieve the second record' do
76
+ it { expect(subject.retrieve_record(2)).to be_correctly_sent }
77
+ it { expect(subject).to respond_with_status 200 }
78
+ it { expect(subject).to respond_a_record }
79
+ it { expect(subject).to respond_with_data $second_obj}
80
+ end
81
+
82
+ end
@@ -0,0 +1,3 @@
1
+ require 'rack-rest-rspec/prepare'
2
+
3
+
data/main.rb ADDED
File without changes
data/spec/table.txt ADDED
@@ -0,0 +1,11 @@
1
+ CREATE TABLE COMPANY(
2
+ ID INT PRIMARY KEY NOT NULL,
3
+ NAME TEXT NOT NULL,
4
+ AGE INT NOT NULL,
5
+ ADDRESS CHAR(50),
6
+ SALARY REAL,
7
+ DATE DATETIME
8
+ );
9
+
10
+
11
+ CREATE TABLE "othermovies" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "title" VARCHAR(50), "director" VARCHAR(50), "synopsis" TEXT, "year" VARCHAR(50), "date" DATETIME, "indice" INTEGER, "float" FLOAT);
@@ -0,0 +1,25 @@
1
+ AssignmentInConditionalCheck:
2
+ CaseMissingElseCheck:
3
+ ClassLineCountCheck:
4
+ line_count: 300
5
+ ClassNameCheck:
6
+ pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
7
+ #ClassVariableCheck:
8
+ CyclomaticComplexityBlockCheck:
9
+ complexity: 5
10
+ CyclomaticComplexityMethodCheck:
11
+ complexity: 10
12
+ EmptyRescueBodyCheck:
13
+ ForLoopCheck:
14
+ MethodLineCountCheck:
15
+ line_count: 30
16
+ MethodNameCheck:
17
+ pattern: !ruby/regexp /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
18
+ # MissingForeignKeyIndexCheck:
19
+ ModuleLineCountCheck:
20
+ line_count: 500
21
+ ModuleNameCheck:
22
+ pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
23
+ ParameterNumberCheck:
24
+ parameter_count: 5
25
+
metadata ADDED
@@ -0,0 +1,330 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clearest
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1a
5
+ prerelease: 5
6
+ platform: ruby
7
+ authors:
8
+ - Romain GEORGES
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-11-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.5'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.5'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.14'
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: 2.14.1
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ~>
63
+ - !ruby/object:Gem::Version
64
+ version: '2.14'
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: 2.14.1
68
+ - !ruby/object:Gem::Dependency
69
+ name: yard
70
+ requirement: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '0.8'
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: 0.8.7.2
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ~>
85
+ - !ruby/object:Gem::Version
86
+ version: '0.8'
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: 0.8.7.2
90
+ - !ruby/object:Gem::Dependency
91
+ name: rdoc
92
+ requirement: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ~>
96
+ - !ruby/object:Gem::Version
97
+ version: '4.0'
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: 4.0.1
101
+ type: :development
102
+ prerelease: false
103
+ version_requirements: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ~>
107
+ - !ruby/object:Gem::Version
108
+ version: '4.0'
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: 4.0.1
112
+ - !ruby/object:Gem::Dependency
113
+ name: roodi
114
+ requirement: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ~>
118
+ - !ruby/object:Gem::Version
119
+ version: '3.1'
120
+ - - ! '>='
121
+ - !ruby/object:Gem::Version
122
+ version: 3.1.1
123
+ type: :development
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ~>
129
+ - !ruby/object:Gem::Version
130
+ version: '3.1'
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: 3.1.1
134
+ - !ruby/object:Gem::Dependency
135
+ name: code_statistics
136
+ requirement: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: '0.2'
142
+ - - ! '>='
143
+ - !ruby/object:Gem::Version
144
+ version: 0.2.13
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ none: false
149
+ requirements:
150
+ - - ~>
151
+ - !ruby/object:Gem::Version
152
+ version: '0.2'
153
+ - - ! '>='
154
+ - !ruby/object:Gem::Version
155
+ version: 0.2.13
156
+ - !ruby/object:Gem::Dependency
157
+ name: yard-rspec
158
+ requirement: !ruby/object:Gem::Requirement
159
+ none: false
160
+ requirements:
161
+ - - ~>
162
+ - !ruby/object:Gem::Version
163
+ version: '0.1'
164
+ type: :development
165
+ prerelease: false
166
+ version_requirements: !ruby/object:Gem::Requirement
167
+ none: false
168
+ requirements:
169
+ - - ~>
170
+ - !ruby/object:Gem::Version
171
+ version: '0.1'
172
+ - !ruby/object:Gem::Dependency
173
+ name: dm-is-reflective
174
+ requirement: !ruby/object:Gem::Requirement
175
+ none: false
176
+ requirements:
177
+ - - '='
178
+ - !ruby/object:Gem::Version
179
+ version: 1.2.0
180
+ type: :runtime
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ none: false
184
+ requirements:
185
+ - - '='
186
+ - !ruby/object:Gem::Version
187
+ version: 1.2.0
188
+ - !ruby/object:Gem::Dependency
189
+ name: colorize
190
+ requirement: !ruby/object:Gem::Requirement
191
+ none: false
192
+ requirements:
193
+ - - ~>
194
+ - !ruby/object:Gem::Version
195
+ version: '0.7'
196
+ - - ! '>='
197
+ - !ruby/object:Gem::Version
198
+ version: 0.7.7
199
+ type: :runtime
200
+ prerelease: false
201
+ version_requirements: !ruby/object:Gem::Requirement
202
+ none: false
203
+ requirements:
204
+ - - ~>
205
+ - !ruby/object:Gem::Version
206
+ version: '0.7'
207
+ - - ! '>='
208
+ - !ruby/object:Gem::Version
209
+ version: 0.7.7
210
+ - !ruby/object:Gem::Dependency
211
+ name: dm-sqlite-adapter
212
+ requirement: !ruby/object:Gem::Requirement
213
+ none: false
214
+ requirements:
215
+ - - ~>
216
+ - !ruby/object:Gem::Version
217
+ version: '1.2'
218
+ - - ! '>='
219
+ - !ruby/object:Gem::Version
220
+ version: 1.2.0
221
+ type: :runtime
222
+ prerelease: false
223
+ version_requirements: !ruby/object:Gem::Requirement
224
+ none: false
225
+ requirements:
226
+ - - ~>
227
+ - !ruby/object:Gem::Version
228
+ version: '1.2'
229
+ - - ! '>='
230
+ - !ruby/object:Gem::Version
231
+ version: 1.2.0
232
+ - !ruby/object:Gem::Dependency
233
+ name: version
234
+ requirement: !ruby/object:Gem::Requirement
235
+ none: false
236
+ requirements:
237
+ - - ~>
238
+ - !ruby/object:Gem::Version
239
+ version: '1.0'
240
+ - - ! '>='
241
+ - !ruby/object:Gem::Version
242
+ version: 1.0.0
243
+ type: :development
244
+ prerelease: false
245
+ version_requirements: !ruby/object:Gem::Requirement
246
+ none: false
247
+ requirements:
248
+ - - ~>
249
+ - !ruby/object:Gem::Version
250
+ version: '1.0'
251
+ - - ! '>='
252
+ - !ruby/object:Gem::Version
253
+ version: 1.0.0
254
+ description: ! 'Clearest: command line tools for REST (Rack/Sinatra) API generation'
255
+ email:
256
+ - romain@ultragreen.net
257
+ executables:
258
+ - clearest
259
+ extensions: []
260
+ extra_rdoc_files: []
261
+ files:
262
+ - .gitignore
263
+ - Gemfile
264
+ - LICENSE.txt
265
+ - README.md
266
+ - Rakefile
267
+ - VERSION
268
+ - bin/clearest
269
+ - clearest.gemspec
270
+ - lib/clearest.rb
271
+ - lib/clearest/builders/1_application.rb
272
+ - lib/clearest/builders/2_services.rb
273
+ - lib/clearest/generators/1_abstract.rb
274
+ - lib/clearest/generators/2_files_copy.rb
275
+ - lib/clearest/generators/3_fixtures.rb
276
+ - lib/clearest/generators/4_folders.rb
277
+ - lib/clearest/generators/5_templates.rb
278
+ - lib/clearest/patches/1_sqlite_adapter_patch.rb
279
+ - lib/clearest/validators/applications.rb
280
+ - lib/clearest/version.rb
281
+ - lib/template.rb
282
+ - lib/template/Gemfile
283
+ - lib/template/README.md
284
+ - lib/template/Rakefile
285
+ - lib/template/config.ru
286
+ - lib/template/helpers/hash.rb
287
+ - lib/template/helpers/init.rb
288
+ - lib/template/helpers/response_format.rb
289
+ - lib/template/main.rb.tpl
290
+ - lib/template/models/TABLE_NAME.rb.tpl
291
+ - lib/template/models/init.rb
292
+ - lib/template/roodi.yml
293
+ - lib/template/routes/TABLE_NAME.rb.tpl
294
+ - lib/template/routes/init.rb
295
+ - lib/template/spec/TABLE_NAME_spec.rb.tpl
296
+ - lib/template/spec/spec_helper.rb
297
+ - main.rb
298
+ - spec/table.txt
299
+ - ultragreen_roodi_coding_convention.yml
300
+ homepage: http://github.com/Ultragreen/clearest
301
+ licenses:
302
+ - BSD-2
303
+ post_install_message:
304
+ rdoc_options: []
305
+ require_paths:
306
+ - lib
307
+ required_ruby_version: !ruby/object:Gem::Requirement
308
+ none: false
309
+ requirements:
310
+ - - ! '>='
311
+ - !ruby/object:Gem::Version
312
+ version: '0'
313
+ segments:
314
+ - 0
315
+ hash: 1768095623213614804
316
+ required_rubygems_version: !ruby/object:Gem::Requirement
317
+ none: false
318
+ requirements:
319
+ - - ! '>'
320
+ - !ruby/object:Gem::Version
321
+ version: 1.3.1
322
+ requirements: []
323
+ rubyforge_project:
324
+ rubygems_version: 1.8.23
325
+ signing_key:
326
+ specification_version: 3
327
+ summary: ! 'Clearest: REST Api/crud generator'
328
+ test_files:
329
+ - spec/table.txt
330
+ has_rdoc: