├── .ruby-version ├── spec ├── features │ ├── mongodb.rb │ └── activerecord.rb ├── julia_spec.rb ├── spec_helper.rb ├── activerecord_helper.rb ├── action_spec.rb └── builder_spec.rb ├── lib ├── julia_builder.rb ├── julia │ ├── version.rb │ ├── action.rb │ └── builder.rb └── julia.rb ├── .rspec ├── Gemfile ├── .gitignore ├── Rakefile ├── bin ├── setup └── console ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.txt ├── julia.gemspec ├── CODE_OF_CONDUCT.md └── README.md /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.0.0 2 | -------------------------------------------------------------------------------- /spec/features/mongodb.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/julia_builder.rb: -------------------------------------------------------------------------------- 1 | require 'julia' 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /lib/julia/version.rb: -------------------------------------------------------------------------------- 1 | module Julia 2 | VERSION = "0.2.2" 3 | end 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in julia.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /lib/julia.rb: -------------------------------------------------------------------------------- 1 | require "julia/version" 2 | require 'julia/builder' 3 | require 'julia/action' 4 | 5 | module Julia 6 | end 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | bundle install 6 | 7 | # Do any other automated setup that you need to do here 8 | -------------------------------------------------------------------------------- /spec/julia_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Julia do 4 | it 'has a version number' do 5 | expect(Julia::VERSION).not_to be nil 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.2.2 4 | - 3.0.0 5 | - jruby 6 | addons: 7 | code_climate: 8 | repo_token: 27bd1448bf5f9392ee41342332293833dcf3d5a3e30db53c9e4aaea4ec743dca 9 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 2 | 3 | require 'simplecov' 4 | 5 | SimpleCov.start do 6 | add_group 'Lib', 'lib' 7 | add_group 'Tests', 'spec' 8 | end 9 | SimpleCov.minimum_coverage 99 10 | 11 | require 'ostruct' 12 | require 'julia' 13 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "julia" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /spec/activerecord_helper.rb: -------------------------------------------------------------------------------- 1 | require 'active_record' 2 | 3 | ActiveRecord::Base.establish_connection( 4 | adapter: 'sqlite3', 5 | database:':memory:' 6 | ) 7 | 8 | unless ActiveRecord::Base.connection.table_exists?(:users) 9 | ActiveRecord::Base.connection.create_table :users do |t| 10 | t.date :dob 11 | t.string :last_name 12 | t.string :name 13 | end 14 | end 15 | 16 | class User < ActiveRecord::Base; end 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Julia Builder changes 2 | 3 | 0.2.2 4 | ----- 5 | 6 | - Make it compatible for ruby 3.0.0 7 | 8 | 0.2.1 9 | ----- 10 | 11 | - Use csv's API to write headers 12 | 13 | 0.2.0 14 | ----- 15 | 16 | - Add the ability to include mixins. See https://github.com/stevenbarragan/julia_builder/issues/8 17 | 18 | 0.1.5 19 | ----------- 20 | 21 | - Speficy ruby 1.9+ as dependency 22 | - Add `columns` method to set up many columns at once 23 | -------------------------------------------------------------------------------- /lib/julia/action.rb: -------------------------------------------------------------------------------- 1 | module Julia 2 | class Action 3 | attr_reader :key, :action, :block 4 | 5 | def initialize(key, action = nil, &block) 6 | @action = action 7 | @block = block 8 | @key = key 9 | end 10 | 11 | def get_value(record, host) 12 | return host.instance_exec(record, &block) if block 13 | return record.instance_exec(&action) if action.is_a? Proc 14 | 15 | record.send [action, key].compact.first 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Steven Barragán 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /spec/features/activerecord.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'activerecord_helper' 3 | 4 | RSpec.describe 'Julia works with active record' do 5 | before :all do 6 | User.create name: 'Steven', last_name: 'Barragan', dob: Date.new(1990, 11, 19) 7 | end 8 | 9 | context 'given column equal to value' do 10 | class Test < Julia::Builder 11 | column :name 12 | end 13 | 14 | it { expect(Test.new(User.all).build).to eq "name\nSteven\n"} 15 | end 16 | 17 | context 'given column name and a value' do 18 | class Test1 < Julia::Builder 19 | column 'Birthday', :dob 20 | end 21 | 22 | it { expect(Test1.new(User.all).build).to eq "Birthday\n1990-11-19\n" } 23 | end 24 | 25 | context 'given a lambda' do 26 | class Test2 < Julia::Builder 27 | column 'Full name', -> { "#{ name.capitalize } #{ last_name.capitalize }"} 28 | end 29 | 30 | it { expect(Test2.new(User.all).build).to eq "Full name\nSteven Barragan\n" } 31 | end 32 | 33 | context 'given a block' do 34 | class Test3 < Julia::Builder 35 | column 'Class name' do |user| 36 | user.class.name 37 | end 38 | end 39 | 40 | it { expect(Test3.new(User.all).build).to eq "Class name\nUser\n" } 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/julia/builder.rb: -------------------------------------------------------------------------------- 1 | require 'csv' 2 | 3 | module Julia 4 | class Builder 5 | attr_reader :collection, :csv_options 6 | 7 | def initialize(collection, csv_options = Hash.new) 8 | @collection, @csv_options = collection, default_options.merge(csv_options) 9 | end 10 | 11 | def default_options 12 | { 13 | headers: columns_config.keys, 14 | write_headers: true 15 | } 16 | end 17 | 18 | def self.column(keyname, action = nil, &block) 19 | columns_config[keyname] = Action.new(keyname, action, &block) 20 | end 21 | 22 | def self.columns(*args) 23 | args.each do |key| 24 | column(key) 25 | end 26 | end 27 | 28 | def self.columns_config 29 | @columns_config ||= {} 30 | end 31 | 32 | def build 33 | CSV.generate(**csv_options) do |csv| 34 | collection.each do |record| 35 | csv << columns_config.values.map do |action| 36 | action.get_value(record, self) 37 | end 38 | end 39 | end 40 | end 41 | 42 | def self.build(collection, csv_options = Hash.new) 43 | new(collection, csv_options).build 44 | end 45 | 46 | protected 47 | 48 | def columns_config 49 | self.class.columns_config 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /julia.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'julia/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "julia_builder" 8 | spec.version = Julia::VERSION 9 | spec.authors = ["Steven Barragán"] 10 | spec.email = ["me@steven.mx"] 11 | 12 | spec.summary = %q{Export your queries easily and fast} 13 | spec.description = %q{Create flexible builders to export your queries easier than ever} 14 | spec.homepage = "https://github.com/stevenbarragan/julia_builder" 15 | spec.license = "MIT" 16 | 17 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 18 | spec.bindir = "exe" 19 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 20 | spec.require_paths = ["lib"] 21 | 22 | spec.required_ruby_version = '>= 1.9' 23 | 24 | spec.add_development_dependency "bundler" 25 | spec.add_development_dependency "rake" 26 | spec.add_development_dependency "rspec" 27 | spec.add_development_dependency "activerecord" 28 | spec.add_development_dependency "simplecov" 29 | spec.add_development_dependency "sqlite3" unless RUBY_PLATFORM == "java" 30 | spec.add_development_dependency "activerecord-jdbcsqlite3-adapter" if RUBY_PLATFORM == "java" 31 | end 32 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. 6 | 7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 8 | 9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 10 | 11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 12 | 13 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 14 | -------------------------------------------------------------------------------- /spec/action_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Julia::Action do 4 | class TestHost 5 | def add_steven(string) 6 | "#{ string } steven" 7 | end 8 | end 9 | 10 | let(:host){ TestHost.new } 11 | 12 | let(:record) do 13 | OpenStruct.new( 14 | name: 'steven', 15 | last_name: 'barragan', 16 | dob: Date.new(1990, 11, 19) 17 | ) 18 | end 19 | 20 | describe '#get_value' do 21 | context 'given a key' do 22 | let(:subject){ described_class.new(:name) } 23 | 24 | it 'returns key\'s value' do 25 | expect(subject.get_value(record, host)).to eq "steven" 26 | end 27 | end 28 | 29 | context 'given a action\'s and value' do 30 | let(:subject){ described_class.new('Last name', :last_name) } 31 | 32 | it 'returns action\'s value' do 33 | expect(subject.get_value(record, host)).to eq "barragan" 34 | end 35 | end 36 | 37 | context 'given a block' do 38 | let(:subject) do 39 | described_class.new(:name) do |user| 40 | user.last_name 41 | end 42 | end 43 | 44 | it 'returns block\'s value' do 45 | expect(subject.get_value(record, host)).to eq "barragan" 46 | end 47 | 48 | context "using and mixing" do 49 | let(:subject) do 50 | described_class.new(:name) do |user| 51 | add_steven(user.last_name) 52 | end 53 | end 54 | 55 | it "returns block's value using a metod from the mixing" do 56 | expect(subject.get_value(record, host)).to eq "barragan steven" 57 | end 58 | end 59 | end 60 | 61 | context 'given a lambda' do 62 | let(:subject) { described_class.new('full name', -> { "#{name} #{last_name}" }) } 63 | 64 | it 'returns lambda\'s value' do 65 | expect(subject.get_value(record, host)).to eq "steven barragan" 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /spec/builder_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Julia::Builder do 4 | let(:dob){ Time.now } 5 | let(:query) do 6 | [ 7 | OpenStruct.new({ 8 | name: 'steven', 9 | last_name: 'barragan', 10 | dob: dob 11 | }) 12 | ] 13 | end 14 | 15 | class Test1 < described_class 16 | column :name 17 | end 18 | 19 | class Test2 < described_class 20 | column 'Birthday', :dob 21 | end 22 | 23 | class Test3 < described_class 24 | column 'Full name', -> { "#{ name.capitalize } #{ last_name.capitalize }"} 25 | end 26 | 27 | context 'with header name equals to value' do 28 | let(:subject){ Test1.new(query) } 29 | 30 | it { expect(subject.build).to eq "name\nsteven\n" } 31 | end 32 | 33 | context 'with header different than the value' do 34 | let(:subject){ Test2.new(query) } 35 | 36 | it { expect(subject.build).to eq "Birthday\n#{ dob }\n" } 37 | end 38 | 39 | context 'with header different than the value' do 40 | let(:subject){ Test3.new(query) } 41 | 42 | it { expect(subject.build).to eq "Full name\nSteven Barragan\n" } 43 | end 44 | 45 | context 'with csv options' do 46 | let(:csv_options){ {col_sep: ','} } 47 | let(:subject){ Test1.new(query, csv_options) } 48 | 49 | it 'pass csv options' do 50 | expect(CSV).to receive(:generate).with( 51 | subject.default_options.merge(csv_options) 52 | ) 53 | 54 | subject.build 55 | end 56 | 57 | context 'with an option to not write headers' do 58 | let(:subject){ Test3.new(query, write_headers: false) } 59 | 60 | it { expect(subject.build).to eq "Steven Barragan\n" } 61 | end 62 | end 63 | 64 | context 'given a block' do 65 | class Test < described_class 66 | column 'Capital name' do |user| 67 | user.name.capitalize 68 | end 69 | end 70 | 71 | let(:subject){ Test.new(query) } 72 | 73 | it { expect(subject.build).to eq "Capital name\nSteven\n" } 74 | end 75 | 76 | describe '.column' do 77 | let(:block) { ->{ name } } 78 | 79 | it 'creates an action' do 80 | described_class.column(:key, :value, &block) 81 | 82 | columns = described_class.columns_config 83 | expect(columns).to be_include :key 84 | 85 | action = columns[:key] 86 | 87 | expect(action.key).to eq :key 88 | expect(action.action).to eq :value 89 | expect(action.block).to eq block 90 | end 91 | end 92 | 93 | describe '.columns' do 94 | it 'set up each column' do 95 | expect(described_class).to receive(:column).with :name 96 | expect(described_class).to receive(:column).with :lastname 97 | 98 | described_class.columns(:name, :lastname) 99 | end 100 | end 101 | 102 | describe '.build' do 103 | let(:builder){ double('builder') } 104 | 105 | it 'creates a new intance and calls build' do 106 | expect(described_class).to receive(:new).with(query, {}).and_return builder 107 | expect(builder).to receive(:build) 108 | 109 | Test1.build(query) 110 | end 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Julia Builder 2 | [![Gem Version](https://badge.fury.io/rb/julia_builder.svg)](https://badge.fury.io/rb/julia_builder) 3 | [![Build Status](https://travis-ci.org/stevenbarragan/julia_builder.svg?branch=master)](https://travis-ci.org/stevenbarragan/julia_builder) 4 | [![Code Climate](https://codeclimate.com/github/stevenbarragan/julia_builder/badges/gpa.svg)](https://codeclimate.com/github/stevenbarragan/julia_builder) 5 | [![Test Coverage](https://codeclimate.com/github/stevenbarragan/julia_builder/badges/coverage.svg)](https://codeclimate.com/github/stevenbarragan/julia_builder/coverage) 6 | 7 | Julia helps you out to create flexible builders to easily export your queries to csv (for now). 8 | 9 | ## Installation 10 | 11 | Add this line to your application's Gemfile: 12 | 13 | ```ruby 14 | gem 'julia_builder' 15 | ``` 16 | 17 | And then execute: 18 | 19 | $ bundle 20 | 21 | Or install it yourself as: 22 | 23 | $ gem install julia_builder 24 | 25 | For non rails projects 26 | 27 | ```ruby 28 | require 'julia' 29 | ``` 30 | 31 | ## Usage 32 | 33 | 1. Create your own builder class, inherit from `Julia::Builder` and configure your csv columns. 34 | 35 | ```ruby 36 | class UserCsv < Julia::Builder 37 | # specify column's header and value 38 | column 'Birthday', :dob 39 | # header equals 'Birthday' and the value will be on `user.dbo` 40 | 41 | # when header and value are the same, no need to duplicate it. 42 | column :name 43 | # header equals 'name', value will be `user.name` 44 | 45 | # specify many columns at once 46 | columns :name, :lastname, :dob 47 | 48 | # when you need to do some extra work on the value you can pass a proc. 49 | column 'Full name', -> { "#{ name.capitalize } #{ last_name.capitalize }" } 50 | 51 | # or you can pass a block 52 | column 'Type' do |user| 53 | user.class.name 54 | end 55 | 56 | # include your own mixins 57 | include ActionView::Helpers::DateHelper 58 | 59 | column 'age' do |c| 60 | time_ago_in_words c.dob 61 | end 62 | end 63 | ``` 64 | 65 | 2. Now you can use your builder to generate your csv out of a query like: 66 | 67 | ```ruby 68 | users = User.all 69 | UserCsv.build(users) 70 | 71 | # or 72 | UserCsv.build(users, ) 73 | 74 | # e.g. no headers 75 | UserCsv.build(users, write_headers: false) 76 | ``` 77 | 78 | Csv options could be anything [CSV::new](http://ruby-doc.org/stdlib-2.0.0/libdoc/csv/rdoc/CSV.html#method-c-new) understands, but they are optional. 79 | 80 | 3. Enjoy 81 | 82 | ## Development 83 | 84 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 85 | 86 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 87 | 88 | ## Contributing 89 | 90 | Bug reports and pull requests are welcome on GitHub at https://github.com/stevenbarragan/julia_builder/issues. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org/) code of conduct. 91 | 92 | 93 | ## License 94 | 95 | The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 96 | 97 | --------------------------------------------------------------------------------